tags:

views:

2547

answers:

7

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  A: 

The map[key] = value syntax hides the fact that you are explicitly expecting to insert something into the map, rather than changing the value associated with an existing key.

It's somewhat analogous to the reason why some languages require that you declare variables before assigning to them, while others don't, e.g.:

x = 10

(OK in Python.) Versus:

var x = 10;

The var is required in Javascript.

Barry Kelly
I don't think the var is required in JavaScript.
cdmckay
Yup, it definitely is not: https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Variables#Declaring_Variables
cdmckay
+12  A: 

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.

Greg Rogers
make_pair may require a copy constructor - that would be worse than default one. +1 anyway.
Arkadiy
The main thing is, as you said, that they have different semantics. So neither is better than the other, just use the one that does what you need.
jalf
Why would the copy constructor be worse than the default constructor followed by assignment? If it is, then the person who wrote the class has missed something, because whatever operator= does, they should have done the same in the copy constructor.
Steve Jessop
Sometimes default constructing is as expensive as the assignment itself. Naturally assignment and copy construction will be equivalent.
Greg Rogers
+6  A: 

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.

:)

Torlack
Second! Gotta mark this up. Too many folks trade off obtuseness for nano-second speedups. Have mercy on us poor souls that must maintain such atrocities!
Mr.Ree
As Greg Rogers wrote: "The two have different semantics when it comes to the key already existing in the map. So they aren't really directly comparable."
dalle
+22  A: 

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(std::make_pair(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.

netjeff
Should be noted that map::insert never replaces values. And in the general case I would say that it is better to use `(res.first)->second` instead of `value` also in the second case.
dalle
I updated to be more clear that map::insert never replaces. I left the `else` because I think using `value` is clearer than than the iterator. Only if the value's type had an unusual copy ctor or op== would it be different, and that type would cause other issues using STL containers like map.
netjeff
+2  A: 

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.

Anthony Cramp
+1  A: 

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);
}
rlbond
A: 

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.

Hawkeye Parker