2017-02-25 12 views
3

基本的には、std::unordered_map<std::string, std::variant<unsigned, /*self_type*/>>のようなコンテナがいくつかあります。このコンテナでは、符号なしの値は終端ノードであり、self_typeはサブツリーを表し、終端ノードまでさらに検索されます。未知の再帰レベルを持つネストされた連想配列の初期化をクリアする

これは、1つの追加のラッパークラスで実装できます。

struct node { 
    std::unordered_map<std::string, 
         std::variant<unsigned, std::unique_ptr<node>>> children; 
}; 

十分なフェアが、私は、ネストされた初期化子リストで、通常のstd::unordered_mapとして、それを初期化したいと思います。例:

{ 
    { 
     "str1", 
     { 
      {"strstr1", 1}, 
      {"strstr2", 2} 
     } 
    }, 
    {"str2", 3} 
} 

さらに適切なデータ構造の提案も歓迎します。

+0

あなたは木のツリーを必要としますか?たとえば、すべてのデータに対して1つのツリーを使用し、2つの「別々の」ツリー間の「パーティション」であるかどうかを示すために、各ノードに追加された追加情報を使用できます。 –

+0

@JohnZwinck私は本当に木の木を必要としません。木の木は純粋な木構造で自然です。私が純粋なノードベースのツリー構造を使用するならば、それはより簡単になります。主に性能問題のためにunordered_mapを選択し、ツリーを平坦化しました。しかし、これをしても実際にはより良いパフォーマンスが得られないかもしれないことを示唆しています。 – YiFei

+0

余分な '{}'はどうですか? – Yakk

答えて

3

ソリューション1 - ラッパークラスを使用して:私は利用可能std::variantを持っていないので、

struct node { 
    using myvar = boost::variant< unsigned, boost::recursive_wrapper<node> >; 
    using childmap = std::unordered_map< std::string, myvar >; 

    node() {} 

    node(std::initializer_list<childmap::value_type> il) : 
     children(il) {} 

    childmap children; 
}; 

は、私がここにブースト::バリアントを使用しています。 boost::variantには通常完全なタイプが必要ですが、この時点ではnodeはまだ不完全であるため、boost::recursive_wrapperが必要です。

boost::recursive_wrapperは何も魔法です。それはポインタの周りのラッパーです!わかっているように、問題のない不完全な型のポインタを宣言することができます。このラッパークラスは、割り当て、解放、値セマンティクスの世話をすることによってポインタが使用されるという事実を隠すだけです。 boost::variantによって特別なサポートが提供され、ラッパーを完全に透過的にするので、ラッパークラスがまったくないかのようにバリアントを使用することができます。

使用法:バリアントのコンストラクタは、ネストされた初期化子リストから種類を推測することはできませんので

node n { 
    { "foo", 1 }, 
    { "bar", 2 }, 
    { "baz", node { 
     { "bim", 3 }, 
     { "bam", 4 }} 
    } 
}; 

n.children[ "fum" ] = 5; 
n.children[ "fup" ] = node{{ "fap", 6 }}; 

初期化子リストで明示的に「ノード」が必要です。

デモ:http://coliru.stacked-crooked.com/a/123c59a3523c39ed

ソリューション2 - unordered_map由来:

これは、 "子供" メンバーの必要性を取り除きます。

struct nodemap : 
    std::unordered_map< 
     std::string, 
     boost::variant< unsigned, boost::recursive_wrapper<nodemap> > > 
{ 
    using base = std::unordered_map< 
     std::string, 
     boost::variant< unsigned, boost::recursive_wrapper<nodemap> > >; 

    // Forward all constructors of the base class. 
    using base::base; 
}; 

使用法:

nodemap n{ 
    { "foo", 1 }, 
    { "bar", 2 }, 
    { "baz", nodemap{ 
     { "bim", 3 }, 
     { "bam", 4 }} 
    }}; 

n[ "fum" ] = 5; 
n[ "fup" ] = nodemap{{ "fap", 6 }}; 

詳しい使用例:

// Add something to a child nodemap. 
boost::get<nodemap>(n[ "baz" ])[ "fap" ] = 7; 

// This will throw a boost::bad_get exception because n[ "foo" ] is not a nodemap. 
//boost::get<nodemap>(n[ "foo" ])[ "fap" ] = 8; 

// To avoid this problem, we can check if the child actually is a nodemap: 
if(nodemap* pn = boost::get<nodemap>(&n[ "foo" ])) 
{ 
    (*pn)[ "fap" ] = 8; 
} 

デモ:http://coliru.stacked-crooked.com/a/69914ec5646129f2

+0

マップが完全な型を必要としないのはなぜですか?つまり、unordered_mapが使用されていると、痛いノード:::createを明示的に使用する必要がありますか? – YiFei

+0

http://stackoverflow.com/a/8487164/7571258 std :: mapは、要素をコピーする必要がない別のストレージモデルを使用しているようです。デストラクタ内の子要素のノード要素を削除すると、 'node :: create'呼び出しを' new node'に置き換えることができます。コピーされたノードの重複削除を防ぐために、カスタムコピーコンストラクタと代入演算子を指定するか、これらを "= delete"とマークして使用できないようにしてください。 – zett42

+0

これはまだ私を何時間も悩ませました...私が最終的にunordered_mapの解決策を見つけるまで。結論:C++は素晴らしかったですが、それは素晴らしいものです! :D – zett42

関連する問題