40

基本的に、フィールド名(文字列)を任意のタイプの値にマップするハッシュマップを保持する MyClass が必要です。この目的のために、タイプと値の情報を保持する別の MyField クラスを作成しました。

これは私がこれまでに持っているものです:

template <typename T>
class MyField {
    T m_Value;
    int m_Size;
}


struct MyClass {
    std::map<string, MyField> fields;   //ERROR!!!
}

しかし、ご覧のとおり、MyField の型パラメーターを指定しなかったため、マップ宣言は失敗します...

だから私はそれが次のようなものでなければならないと思います

std::map< string, MyField<int> > fields;

また

std::map< string, MyField<double> > fields;


しかし、宣言されたマップは特定のタイプの MyField しか保持できないため、明らかにこれは私の目的全体を損ないます..任意のタイプの MyField クラスを保持できるマップが必要です..

これを達成する方法はありますか..?

4

7 に答える 7

38

Blindy の答えは非常に良い (+1) ですが、答えを完成させるだけです。動的継承を使用して、ライブラリを使用せずにそれを行う別の方法があります。

class MyFieldInterface
{
    int m_Size; // of course use appropriate access level in the real code...
    ~MyFieldInterface() = default;
}

template <typename T>
class MyField : public MyFieldInterface {
    T m_Value; 
}


struct MyClass {
    std::map<string, MyFieldInterface* > fields;  
}

長所:

  • どの C++ コーダーにもなじみがある
  • Boost の使用を強制するものではありません (一部のコンテキストでは許可されていません)。

短所:

  • オブジェクトをヒープ/フリーストアに割り当て、値セマンティックではなく参照セマンティックを使用してそれらを操作する必要があります。
  • そのように公開されたパブリック継承は、動的継承の過剰使用と、実際には相互依存しすぎている型に関連する多くの長期的な問題につながる可能性があります。
  • オブジェクトを所有する必要がある場合、破壊を管理する必要があるため、ポインタのベクトルは問題があります。

したがって、可能であれば boost::any または boost::variant をデフォルトとして使用し、それ以外の場合にのみこのオプションを検討してください。

最後の短所を修正するには、スマート ポインターを使用できます。

struct MyClass {
    std::map<string, std::unique_ptr<MyFieldInterface> > fields;  // or shared_ptr<> if you are sharing ownership
}

ただし、潜在的により問題のある点がまだあります。

new/delete (または make_unique/shared) を使用してオブジェクトを作成する必要があります。これは、実際のオブジェクトが、アロケーター (ほとんどの場合デフォルトの場所) によって提供される任意の場所のフリー ストア (ヒープ) に作成されることを意味します。したがって、オブジェクトのリストを非常に頻繁に参照することは、キャッシュ ミスのために可能な限り高速ではありません。

多相オブジェクトのベクトル図

このリストを可能な限り高速にループするパフォーマンスに関心がある場合(そうでない場合は以下を無視してください)、boost::variant (使用するすべての具象型が既にわかっている場合) またはある種の型消去されたポリモーフィック コンテナーを使用します。

ポリモーフィック コンテナの図

コンテナは同じ型のオブジェクトの配列を管理するが、それでも同じインターフェイスを公開するという考え方です。そのインターフェイスは、概念 (ダックタイピング手法を使用) または動的インターフェイス (最初の例のような基本クラス) のいずれかです。利点は、コンテナが同じタイプのオブジェクトを別々のベクトルに保持するため、それらをすばやく処理できることです。あるタイプから別のタイプに移動するだけではありません。

以下に例を示します (画像はそこからのものです): http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html

ただし、この手法は、オブジェクトが挿入される順序を維持する必要がある場合に役立ちます。

いずれにせよ、いくつかの解決策が考えられますが、それはニーズに大きく依存します。ケースの経験が十分でない場合は、例で最初に説明した単純な解決策か、boost::any/variant のいずれかを使用することをお勧めします。


この回答を補完するものとして、使用できるすべての C++ 型消去手法をコメントと長所/短所とともにまとめた非常に優れたブログ記事を紹介したいと思います。

于 2014-07-11T16:39:44.670 に答える
19

boost::variant(格納できる型がわかっている場合は、コンパイル時のサポートが提供されます)またはboost::any(実際にはすべての型に使用できますが、そうである可能性はほとんどありません)のいずれかを使用してください。

http://www.boost.org/doc/libs/1_55_0/doc/html/variant/misc.html#variant.versus-any

編集:独自のソリューションを展開することはクールに見えるかもしれませんが、完全で適切な実装を使用すると、長期的には頭痛の種が大幅に軽減されることを強調することはできません。boost::anyRHS コピー コンストラクター (C++11) を実装し、安全な ( typeid()) と安全でない (ダム キャスト) の両方の値の取得、const正確性、RHS オペランド、およびポインターと値の両方の型を使用します。

これは一般に当てはまりますが、アプリケーション全体を構築する低レベルの基本型の場合はなおさらです。

于 2014-07-11T16:21:49.680 に答える
1

また、void* を使用し、reinterpret_cast を使用して値を正しい型にキャストすることもできます。これは、コールバックで C でよく使用される手法です。

#include <iostream>
#include <unordered_map>
#include <string>
#include <cstdint> // Needed for intptr_t
using namespace std;


enum TypeID {
    TYPE_INT,
    TYPE_CHAR_PTR,
    TYPE_MYFIELD
};    

struct MyField {
    int typeId;
    void * data;
};

int main() {

    std::unordered_map<std::string, MyField> map;

    MyField anInt = {TYPE_INT, reinterpret_cast<void*>(42) };

    char cstr[] = "Jolly good";
    MyField aCString = { TYPE_CHAR_PTR, cstr };

    MyField aStruct  = { TYPE_MYFIELD, &anInt };

    map.emplace( "Int", anInt );
    map.emplace( "C String", aCString );
    map.emplace( "MyField" , aStruct  );  

    int         intval   = static_cast<int>(reinterpret_cast<intptr_t>(map["Int"].data)); 
    const char *cstr2    = reinterpret_cast<const char *>( map["C String"].data );
    MyField*    myStruct = reinterpret_cast<MyField*>( map["MyField"].data );

    cout << intval << '\n'
         << cstr << '\n'
         << myStruct->typeId << ": " << static_cast<int>(reinterpret_cast<intptr_t>(myStruct->data)) << endl;
}
于 2014-07-11T16:46:35.987 に答える