207

少し前に、STLマップに値を挿入する方法について同僚と話し合いました。彼が好んだのに対し、私はmap[key] = value;それが自然に感じられ、読みやすいので好んだmap.insert(std::make_pair(key, value)).

私はちょうど彼に尋ねたところ、インサートが優れている理由を私たちのどちらも覚えていませんが、単にスタイルの好みではなく、効率などの技術的な理由があったと確信しています. SGI STL リファレンスには、「厳密に言えば、このメンバー関数は不要です。便宜上のみ存在します」と書かれています。

誰かその理由を教えてもらえますか、それとも私が夢見ているだけですか?

4

13 に答える 13

243

あなたが書くとき

map[key] = value;

forを置き換えたのか、 で新しく作成したのかを判断する方法はありません。valuekeykeyvalue

map::insert()のみを作成します:

using std::cout; using std::endl;
typedef std::map<int, std::string> MyMap;
MyMap map;
// ...
std::pair<MyMap::iterator, bool> res = map.insert(MyMap::value_type(key,value));
if ( ! res.second ) {
    cout << "key " <<  key << " already exists "
         << " with value " << (res.first)->second << endl;
} else {
    cout << "created key " << key << " with value " << value << endl;
}

ほとんどのアプリでは、通常、作成しているのか置き換えているのか気にしないので、読みやすい方を使用しますmap[key] = value

于 2008-11-29T07:03:10.760 に答える
53

マップに既に存在するキーに関しては、2 つのセマンティクスが異なります。したがって、それらは実際には直接比較できません。

しかし、operator[] バージョンでは、デフォルトで値を構築してから代入する必要があるため、これがコピー構築よりもコストがかかる場合は、コストが高くなります。デフォルトの構成が意味をなさない場合があり、その場合、operator[] バージョンを使用することは不可能です。

于 2008-11-28T16:01:34.390 に答える
36

注意すべきもう1つのことstd::map

myMap[nonExistingKey];マップに新しいエントリを作成し、キーを付けnonExistingKeyてデフォルト値に初期化します。

これは、初めて見たとき、私をひどく怖がらせました(厄介なレガシーバグに頭をぶつけながら)。期待していなかったでしょう。私には、これは get 操作のように見えますが、「副作用」があるとは思っていませんでした。map.find()マップから取得するときに優先します。

于 2009-05-13T23:39:24.620 に答える
19

デフォルト コンストラクターのパフォーマンス ヒットが問題にならない場合は、念のため、より読みやすいバージョンを使用してください。

:)

于 2008-11-28T16:11:24.087 に答える
13

アプリケーションの速度が重要な場合は、[] 演算子を使用することをお勧めします。これは、元のオブジェクトの合計 3 つのコピーが作成され、そのうち 2 つが一時オブジェクトであり、遅かれ早かれ破棄されるためです。

しかし、insert() では、元のオブジェクトの 4 つのコピーが作成され、そのうち 3 つが一時オブジェクト (必ずしも「一時」であるとは限りません) であり、破棄されます。

1. 1 つのオブジェクトのメモリ割り当て 2. 1 つの追加のコンストラクタ呼び出し 3. 1 つの追加のデストラクタの呼び出し 4. 1 つのオブジェクトのメモリ割り当て解除

オブジェクトが大きい場合、コンストラクターが典型的であり、デストラクタは多くのリソースを解放し、上記のポイントはさらにカウントされます。読みやすさに関しては、どちらも十分に公平だと思います。

同じ質問が頭に浮かびましたが、読みやすさではなく速度についてでした。これは、私が言及したポイントについて知るようになったサンプルコードです。

class Sample
{
    static int _noOfObjects;

    int _objectNo;
public:
    Sample() :
        _objectNo( _noOfObjects++ )
    {
        std::cout<<"Inside default constructor of object "<<_objectNo<<std::endl;
    }

    Sample( const Sample& sample) :
    _objectNo( _noOfObjects++ )
    {
        std::cout<<"Inside copy constructor of object "<<_objectNo<<std::endl;
    }

    ~Sample()
    {
        std::cout<<"Destroying object "<<_objectNo<<std::endl;
    }
};
int Sample::_noOfObjects = 0;


int main(int argc, char* argv[])
{
    Sample sample;
    std::map<int,Sample> map;

    map.insert( std::make_pair<int,Sample>( 1, sample) );
    //map[1] = sample;
    return 0;
}

insert() 使用時の出力 []演算子使用時の出力

于 2013-06-02T13:59:58.247 に答える
9

map :: insert()の落とし穴は、キーがマップにすでに存在する場合、値を置き換えないことです。値が置き換えられるJavaのMap.put()と同じようにinsert()が動作することを期待しているJavaプログラマーによって書かれたC++コードを見てきました。

于 2008-11-29T08:31:46.183 に答える
2

Boost.Assignも使用できることに注意してください。

using namespace std;
using namespace boost::assign; // bring 'map_list_of()' into scope

void something()
{
    map<int,int> my_map = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);
}
于 2009-03-25T15:02:13.497 に答える
1

キーの値が存在する場合は上書きするが、値が存在する場合はoperator[] 上書き.insert しないことを示す別の例を次に示します。

void mapTest()
{
  map<int,float> m;


  for( int i = 0 ; i  <=  2 ; i++ )
  {
    pair<map<int,float>::iterator,bool> result = m.insert( make_pair( 5, (float)i ) ) ;

    if( result.second )
      printf( "%d=>value %f successfully inserted as brand new value\n", result.first->first, result.first->second ) ;
    else
      printf( "! The map already contained %d=>value %f, nothing changed\n", result.first->first, result.first->second ) ;
  }

  puts( "All map values:" ) ;
  for( map<int,float>::iterator iter = m.begin() ; iter !=m.end() ; ++iter )
    printf( "%d=>%f\n", iter->first, iter->second ) ;

  /// now watch this.. 
  m[5]=900.f ; //using operator[] OVERWRITES map values
  puts( "All map values:" ) ;
  for( map<int,float>::iterator iter = m.begin() ; iter !=m.end() ; ++iter )
    printf( "%d=>%f\n", iter->first, iter->second ) ;

}
于 2013-03-18T19:24:59.973 に答える
1

std::mapinsert()関数がキーに関連付けられた値を上書きしないという事実により、次のようなオブジェクト列挙コードを記述できます。

string word;
map<string, size_t> dict;
while(getline(cin, word)) {
    dict.insert(make_pair(word, dict.size()));
}

これは、さまざまな一意でないオブジェクトを範囲 0..N の ID にマップする必要がある場合によくある問題です。これらの ID は、後でグラフ アルゴリズムなどで使用できます。私の意見では、 with の代替はoperator[]読みにくくなります。

string word;
map<string, size_t> dict;
while(getline(cin, word)) {
    size_t sz = dict.size();
    if (!dict.count(word))
        dict[word] = sz; 
} 
于 2015-07-01T22:25:04.973 に答える
0

insert()との違いoperator[]は、他の回答ですでに十分に説明されています。ただし、それぞれC ++11C++17で新しい挿入メソッドstd::mapが導入されました。

「新しい」挿入方法の簡単な要約を示しましょう。

  • emplace()注:正しく使用すると、このメソッドは、挿入される要素を構成することで、不要なコピーまたは移動操作を回避できます。と同様にinsert()、コンテナ内に同じキーを持つ要素がない場合にのみ、要素が挿入されます。
  • insert_or_assign():このメソッドは、 の「改良」バージョンですoperator[]。とは異なりoperator[]insert_or_assign()マップの値の型がデフォルトで構築可能である必要はありません。これにより、たとえばGreg Rogers' answerで言及されている欠点が克服されます。
  • try_emplace():このメソッドは、 の「改良」バージョンですemplace()。とは異なりemplace()try_emplace()キーがマップに既に存在するために挿入が失敗した場合、(移動操作のために) その引数を変更しません。

詳細については、こちらinsert_or_assign()の回答をtry_emplace()ご覧ください。

Coliru の簡単なコード例

于 2020-08-10T12:29:23.560 に答える