tags:

views:

150

answers:

4

The following code works fine :

std::map<int, int>& m = std::map<int, int>();
int i = m[0];

But not the following code :

// error C2678: binary '[' : no operator...
const std::map<int, int>& m = std::map<int, int>();
int i = m[0];

Most of the time, I prefer to make most of my stuff to become immutable, due to reason :

http://www.javapractices.com/topic/TopicAction.do?Id=29

I look at map source code. It has

mapped_type& operator[](const key_type& _Keyval)

Is there any reason, why std::map unable to provide

const mapped_type& operator[](const key_type& _Keyval) const
+1  A: 

operator[] inserts if the key is not found, therefore it cannot be a const member function.

Axel Gneiting
+7  A: 

The reason is that std::map semantics state that if you try to access an element at a key that does not exist, the key is created with a default-constructed element. In other words m[0] will create an int at location 0 if one does not already exist. Obviously, that is not compatible with a const map.

You could say "well, make a const version of operator[] and have it not do that!", but there are two problems: the difference in semantics would be non-obvious and confusing, and it's not clear exactly what should happen if you do try to access a key that does not exist (throw an exception?).

Instead, what you should do is use the find() method on the map, which will return an iterator pointing to the key/value pair you're looking for. The lookup is exactly as efficient as operator[], it can be used on const maps (returning a const iterator in that case), and it will return the end() iterator if the key does not exist.

Tyler McHenry
I am not sure to subscribe to the `confusing` argument, for as demonstrated by this user it is confusing as it is too. And `map.find(key)->second` is not as nice as `map[key]` is. With all the `undefined` behavior already in, one more would not be that remiss :p
Matthieu M.
It's confusing as long as you are not used to it. It is probably not desirable to have the same function have completely different semantics: `template <class T> void foo(T }` `foo(somemap); //may add default items` `foo(some_constmap); //may throw`. You could make a freestanding utility function that only performs (const or non-const) look-up (might have been better if the map had this).
visitor
As to allowing undefined behavior here, this means that with a map of unknown contents, you'd always have to check first before look-up to avoid it, which might not be a particularly smart usage pattern.
visitor
@Matthieu I'd agree that the current behavior is not *intuitive*, but it's consistent and therefore not confusing once you've found out what it is. Having two versions of `operator[]`, one of which is guaranteed to always work and the other of which may throw an exception or cause undefined behavior in some circumstances, and having the only difference be whether or not the underlying object is const, would be confusing because changing the const-ness of the variable elsewhere could drastically alter the program's behavior without causing any compile-time errors or warnings.
Tyler McHenry
I understand your point, effectively I've always found `Boost Serialization` a bit annoying because the only difference between serialization and deserialization is the const-ness of the operator...
Matthieu M.
+2  A: 

It does have an immutable version, and it's called find().

280Z28
+3  A: 

operator[] will create the entry if it does not exist in the map. This is not possible if the operator is implemented for a const map. This is the explanation given in The C++ Programming Language:

Subscripting a map adds a default element when the key is not found. Therefore, there is no version of operator[] for const maps. Furthermore, subscripting can be used only if the mapped_type (value type) has a default value. If the programmer simply wants to see if a key is present, the find() operation (§17.4.1.6) can be used to locate a key without modifying the map.

Vijay Mathew