C++ マップの insert と emplace と operator[] の違い 質問する

C++ マップの insert と emplace と operator[] の違い 質問する

初めてマップを使用していますが、要素を挿入する方法がたくさんあることに気付きました。 、 、 のほか、 や などのバリエーションを使用できますemplace()operator[]これらすべてinsert()に関する情報や特定のケースに関する質問はたくさんありますが、全体像はまだ理解できません。そこで、私の質問は次の 2 つです。value_typemake_pair

  1. それぞれが他と比べて優れている点は何でしょうか?

  2. emplace を標準に追加する必要がありましたか? emplace なしではこれまで不可能だったことはありますか?

ベストアンサー1

マップの特定のケースでは、以前のオプションはoperator[]insert( の異なる種類insert) の 2 つだけでした。そこで、これらについて説明を始めます。

、検索または追加operator[]演算子です。マップ内で指定されたキーを持つ要素を検索し、存在する場合は、格納されている値への参照を返します。存在しない場合は、デフォルトの初期化で新しい要素を作成し、その場所に挿入して、その参照を返します。

関数insert(単一要素の場合) はvalue_type( std::pair<const Key,Value>) を受け取り、キー (firstメンバー) を使用して挿入を試みます。std::mapは重複を許可しないため、既存の要素がある場合は何も挿入されません。

2 つの関数の最初の違いは、 はデフォルトで初期化されたoperator[]を構築できる必要があるため、デフォルトで初期化できない値型には使用できません。2 つの関数の 2 つ目の違いは、指定されたキーを持つ要素がすでに存在する場合に何が起こるかです。関数はマップの状態を変更せず、代わりに要素への反復子 (および挿入されなかったことを示す ) を返します。insertfalse

// assume m is std::map<int,int> already has an element with key 5 and value 0
m[5] = 10;                      // postcondition: m[5] == 10
m.insert(std::make_pair(5,15)); // m[5] is still 10

の場合insert、引数は のオブジェクトでありvalue_type、さまざまな方法で作成できます。適切な型を使用して直接構築することも、 をvalue_type構築できる任意のオブジェクトを渡すこともできます。ここで がstd::make_pair役立ちます。これにより、オブジェクトを簡単に作成できますstd::pairが、おそらくこれは必要な方法ではないでしょう...

次の呼び出しの純粋な効果は同様です。

K t; V u;
std::map<K,V> m;           // std::map<K,V>::value_type is std::pair<const K,V>
    
m.insert( std::pair<const K,V>(t,u) );      // 1
m.insert( std::map<K,V>::value_type(t,u) ); // 2
m.insert( std::make_pair(t,u) );            // 3

しかし、それらは実際には同じではありません... [1] と [2] は実際には同等です。 どちらの場合も、コードは同じ型 ( std::pair<const K,V>) の一時オブジェクトを作成し、それを関数に渡しますinsertinsert関数はバイナリ検索ツリーに適切なノードを作成し、value_type引数からノードに部分をコピーします。 を使用する利点はvalue_type、 がvalue_type常に と一致する value_typeため、引数の型を誤って入力できないことですstd::pair

違いは[3]にあります。関数はstd::make_pairテンプレート関数であり、を作成しますstd::pair。シグネチャは次のとおりです。

template <typename T, typename U>
std::pair<T,U> make_pair(T const & t, U const & u );

一般的な使用法であるため、意図的に へのテンプレート引数を提供していませんstd::make_pair。つまり、テンプレート引数は の呼び出しから推測され、この場合は になるためT==K,U==V、 の呼び出しstd::make_pairは を返しますstd::pair<K,V>( がないことに注意してくださいconst)。シグネチャでは、が の呼び出しからの戻り値に近いvalue_typeが、同じではないことが必要です。十分に近いため、 は正しい型の一時ファイルを作成し、それをコピーして初期化します。これがノードにコピーされ、合計 2 つのコピーが作成されます。std::make_pair

これは、テンプレート引数を指定することで修正できます。

m.insert( std::make_pair<const K,V>(t,u) );  // 4

しかし、case [1]で型を明示的に入力するのと同じように、それでもエラーが発生しやすくなります。

ここまで、外部でinsertを作成しvalue_type、そのオブジェクトをコンテナにコピーする必要がある、 を呼び出すさまざまな方法を紹介してきました。 または、operator[]型がデフォルトで構築可能かつ割り当て可能(意図的に のみに焦点を当てるm[k]=v) である場合は を使用することもできます。この場合、1 つのオブジェクトのデフォルトの初期化と、そのオブジェクトへの値のコピーが必要になります。

C++11 では、可変長テンプレートと完全転送により、配置(その場で作成)によってコンテナーに要素を追加する新しい方法があります。さまざまなコンテナー内の関数は基本的に同じことを行います。コンテナーにコピーするソースを取得emplaceする代わりに、関数はコンテナーに格納されているオブジェクトのコンストラクターに転送されるパラメーターを受け取ります。

m.emplace(t,u);               // 5

[5]では、 はstd::pair<const K, V>作成されて に渡されるのではなくemplace、 およびオブジェクトへの参照がtu渡されemplace、データ構造内のサブオブジェクトのコンストラクタに転送されますvalue_type。 この場合、のコピーはまったく行われませstd::pair<const K,V>。これが、C++03の代替手段に対する の利点ですemplace。 の場合と同様に、insertマップ内の値はオーバーライドされません。


私が考えたことのない興味深い質問は、emplaceマップに対して実際にどのように実装できるかということですが、これは一般的なケースでは単純な問題ではありません。

おすすめ記事