STLマップでは、map :: insertを使用するほうが[]よりも良いですか?

StackOverflow https://stackoverflow.com/questions/326062

  •  11-07-2019
  •  | 
  •  

質問

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

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

誰もがその理由を教えてもらえますか、それとも存在することを夢見ていますか?

役に立ちましたか?

解決

書くとき

map[key] = value;

key value 置き換えしたか、新しい作成したかを判断する方法はありません value を持つ key

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 を使用します。

他のヒント

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

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

std :: map

で注意すべきもう1つのこと

myMap [nonExistingKey]; は、デフォルト値に初期化された nonExistingKey をキーとする新しいエントリをマップに作成します。

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

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

:)

挿入は、例外の安全性の観点から優れています。

map [key] = value は実際には2つの操作です。

  1. map [key] -デフォルト値でマップ要素を作成します。
  2. = value -値をその要素にコピーします。

2番目のステップで例外が発生する場合があります。その結果、操作は部分的にしか行われません(新しい要素がマップに追加されましたが、その要素は value で初期化されませんでした)。操作が完了していないが、システム状態が変更されている状況は、「副作用」を伴う操作と呼ばれます。

insert 操作は強力な保証を提供し、副作用がないことを意味します( https://en.wikipedia.org/wiki/Exception_safety )。 insert は完全に完了するか、マップを変更されていない状態のままにします。

http://www.cplusplus.com/reference/map/map/insert /

  

単一の要素を挿入する場合、例外の場合にコンテナに変更はありません(強力な保証)。

アプリケーションがスピードクリティカルである場合、[]演算子を使用してアドバイスします。元のオブジェクトのコピーを合計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()使用時の出力 []演算子使用時に出力

c ++ 11でSTLマップにペアを挿入する最良の方法は次のとおりだと思います:

typedef std::map<int, std::string> MyMap;
MyMap map;

auto& result = map.emplace(3,"Hello");

結果は次のものとペアになります:

  • 最初の要素(result.first)、挿入されたペアを指すか、 キーが既に存在する場合、このキーとのペア。

  • 2番目の要素(result.second)、挿入が正しい場合はtrue、または falseで、何かがおかしい。

PS:注文について説明しない場合は、std :: unordered_mapを使用できます;)

ありがとう!

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

1つの注意点は、 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);
}

別の例を示します。 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 ) ;

}

これはかなり制限されたケースですが、受け取ったコメントから判断すると、注目に値すると思います。

過去に、地図を次の形式で使用する人を見てきました

map< const key, const val> Map;

偶発的な値の上書きのケースを回避するために、他のいくつかのコードで書き込みを進めます:

const_cast< T >Map[]=val;

これを行う理由は、これらの特定のコードではマップ値を上書きしないと確信していたためです。そのため、より読みやすいメソッド [] を進めます。

実際にこれらの人々によって書かれたコードから直接的なトラブルは一度もありませんでしたが、今日まで、それらが簡単に回避できる場合、リスクは-どんなに小さくても取るべきではないと強く感じています。

絶対に上書きしてはならないマップ値を処理する場合は、 insert を使用します。読みやすくするためだけに例外を設けないでください。

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

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

異なる非一意オブジェクトを0..Nの範囲のIDにマッピングする必要がある場合、これは非常に一般的な問題です。これらのIDは、たとえばグラフアルゴリズムで後で使用できます。私の意見では、 operator [] を使用した代替は読みにくくなります:

string word;
map<string, size_t> dict;
while(getline(cin, word)) {
    size_t sz = dict.size();
    if (!dict.count(word))
        dict[word] = sz; 
} 
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top