1. ホーム
  2. c++

[解決済み】c++のmapでinsertとemplaceとoperator[]の比較

2022-04-19 06:07:45

質問

初めてマップを使うのですが、要素を挿入する方法がたくさんあることに気づきました。例えば emplace() , operator[] または insert() を使用するようなバリエーションもあります。 value_type または make_pair . どれも多くの情報があり、特定のケースについての質問もありますが、やはり全体像が理解できません。 そこで、2点質問です。

  1. それぞれ、他と比べてどんなメリットがあるのでしょうか?

  2. emplaceを標準搭載する必要性はあったのでしょうか?また、emplaceがなければできなかったことはありますか?

解決方法は?

地図という特殊なケースにおいて、以前の選択肢は2つしかありませんでした。 operator[]insert (の異なるフレーバー)。 insert ). そこで、これらの説明を始めます。

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

insert 関数 (単一要素フレーバーの場合) は value_type ( std::pair<const Key,Value> ) を使用すると、キー ( first メンバー)を挿入しようとする。なぜなら std::map は重複を許さないので、既存の要素がある場合は何も挿入されません。

両者の違いとしてまず挙げられるのは operator[] は、デフォルトで初期化された そのため、デフォルトで初期化できない値型には使えません。両者の第二の違いは,与えられたキーを持つ要素が既に存在する場合にどうなるかである。これは insert 関数はマップの状態を変更せず、その代わりに要素へのイテレータを返します(そして、その要素への false 挿入されなかったことを示す)。

// 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

しかし、この2つは実は同じではないのです...。[1]と[2]は実は同じなのです。どちらの場合も、コードは同じ型の一時的なオブジェクトを作成します ( std::pair<const K,V> ) に渡し、それを insert 関数を使用します。この insert 関数は、バイナリサーチツリー内に適切なノードを作成し、そのノードに 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 というのは 閉じる を呼び出したときの戻り値と同じではありません。 std::make_pair . 十分に近いので、正しい型のテンポラリを作成し、それをコピーして初期化します。それが今度はノードにコピーされ、合計2つのコピーが作成されます。

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

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

しかし、これでは、ケース[1]の明示的な型付けと同じように、やはりエラーになりやすいのです。

ここまでは、さまざまな方法で insert を作成する必要があること。 value_type を外部で作成し、そのオブジェクトをコンテナにコピーします。あるいは operator[] の場合、型は デフォルトコンストラクタブル 割り当て可能 (のみ(意図的に m[k]=v ) が必要で、1つのオブジェクトのデフォルトの初期化と コピー をそのオブジェクトに入れる。

C++11 では、可変個体テンプレートと完全な転送により、コンテナに要素を追加する新しい方法があります。 配置 (create in place)といいます。そのため emplace 関数は、基本的に同じことをします。 ソース から コピー をコンテナに格納する場合、この関数はコンテナに格納されたオブジェクトのコンストラクタに転送されるパラメータを受け取ります。

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

5]では std::pair<const K, V> は作成されず emplace への参照ではなく tu オブジェクトに渡されます。 emplace のコンストラクタに転送します。 value_type サブオブジェクトを作成します。この場合 いいえ のコピーです。 std::pair<const K,V> の利点です。 emplace をC++03の代替品と比較した場合。の場合と同様に insert は、マップの値を上書きすることはありません。


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