In STL maps, is it better to use map::insert than []?

白昼怎懂夜的黑 提交于 2019-11-26 03:01:29

问题


A while ago, I had a discussion with a colleague about how to insert values in STL maps. I preferred map[key] = value; because it feels natural and is clear to read whereas he preferred map.insert(std::make_pair(key, value))

I just asked him and neither of us can remember the reason why insert is better, but I am sure it was not just a style preference rather there was a technical reason such as efficiency. The SGI STL reference simply says \"Strictly speaking, this member function is unnecessary: it exists only for convenience.\"

Can anybody tell me that reason, or am I just dreaming that there is one?


回答1:


When you write

map[key] = value;

there's no way to tell if you replaced the value for key, or if you created a new key with value.

map::insert() will only create:

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;
}

For most of my apps, I usually don't care if I'm creating or replacing, so I use the easier to read map[key] = value.




回答2:


The two have different semantics when it comes to the key already existing in the map. So they aren't really directly comparable.

But the operator[] version requires default constructing the value, and then assigning, so if this is more expensive then copy construction, then it will be more expensive. Sometimes default construction doesn't make sense, and then it would be impossible to use the operator[] version.




回答3:


Another thing to note with std::map:

myMap[nonExistingKey]; will create a new entry in the map, keyed to nonExistingKey initialized to a default value.

This scared the hell out of me the first time I saw it (while banging my head against a nastly legacy bug). Wouldn't have expected it. To me, that looks like a get operation, and I didn't expect the "side-effect." Prefer map.find() when getting from your map.




回答4:


If the performance hit of the default constructor isn't an issue, the please, for the love of god, go with the more readable version.

:)




回答5:


insert is better from the point of exception safety.

The expression map[key] = value is actually two operations:

  1. map[key] - creating a map element with default value.
  2. = value - copying the value into that element.

An exception may happen at the second step. As result the operation will be only partially done (a new element was added into map, but that element was not initialized with value). The situation when an operation is not complete, but the system state is modified, is called the operation with "side effect".

insert operation gives a strong guarantee, means it doesn't have side effects (https://en.wikipedia.org/wiki/Exception_safety). insert is either completely done or it leaves the map in unmodified state.

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

If a single element is to be inserted, there are no changes in the container in case of exception (strong guarantee).




回答6:


If your application is speed critical i will advice using [] operator because it creates total 3 copies of the original object out of which 2 are temporary objects and sooner or later destroyed as.

But in insert(), 4 copies of the original object are created out of which 3 are temporary objects( not necessarily "temporaries") and are destroyed.

Which means extra time for: 1. One objects memory allocation 2. One extra constructor call 3. One extra destructor call 4. One objects memory deallocation

If your objects are large, constructors are typical, destructors do a lot of resource freeing, above points count even more. Regarding readability, i think both are fair enough.

The same question came into my mind but not over readability but speed. Here is a sample code through which I came to know about the point i mentioned.

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;
}




回答7:


Now in c++11 I think that the best way to insert a pair in a STL map is:

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

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

The result will be a pair with:

  • First element (result.first), points to the pair inserted or point to the pair with this key if the key already exist.

  • Second element (result.second), true if the insertion was correct or false it something went wrong.

PS: If you don´t case about the order you can use std::unordered_map ;)

Thanks!




回答8:


A gotcha with map::insert() is that it won't replace a value if the key already exists in the map. I've seen C++ code written by Java programmers where they have expected insert() to behave the same way as Map.put() in Java where values are replaced.




回答9:


One note is that you can also use 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);
}



回答10:


Here's another example, showing that operator[] overwrites the value for the key if it exists, but .insert does not overwrite the value if it exists.

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 ) ;

}



回答11:


This is a rather restricted case, but judging from the comments I've received I think it's worth noting.

I've seen people in the past use maps in the form of

map< const key, const val> Map;

to evade cases of accidental value overwriting, but then go ahead writing in some other bits of code:

const_cast< T >Map[]=val;

Their reason for doing this as I recall was because they were sure that in these certain bits of code they were not going to be overwriting map values; hence, going ahead with the more 'readable' method [].

I've never actually had any direct trouble from the code that was written by these people, but I strongly feel up until today that risks - however small - should not be taken when they can be easily avoided.

In cases where you're dealing with map values that absolutely must not be overwritten, use insert. Don't make exceptions merely for readability.




回答12:


The fact that std::map insert() function doesn't overwrite value associated with the key allows us to write object enumeration code like this:

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

It's a pretty common problem when we need to map different non-unique objects to some id's in range 0..N. Those id's can be later used, for example, in graph algorithms. Alternative with operator[] would look less readable in my opinion:

string word;
map<string, size_t> dict;
while(getline(cin, word)) {
    size_t sz = dict.size();
    if (!dict.count(word))
        dict[word] = sz; 
} 


来源:https://stackoverflow.com/questions/326062/in-stl-maps-is-it-better-to-use-mapinsert-than

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!