Why does std::map operator[] create an object if the key doesn't exist?

Viewed 18578

I'm pretty sure I already saw this question somewhere (comp.lang.c++? Google doesn't seem to find it there either), but a quick search here doesn't seem to find it, so here it is:

Why does std::map operator[] create an object if the key doesn't exist? I don't know but for me this seems counter-intuitive if you compare to most other operator[] (like std::vector) where if you use it you must be sure that the index exists. I'm wondering what's the rationale for implementing this behavior in std::map. Like I said wouldn't it be more intuitive to act more like an index in a vector and crash (well undefined behavior I guess) when accessed with an invalid key?

I've gotten a lot of answers saying basically "it's cheap, so why not?" or similar. I totally agree with that, but why not use a dedicated function for that (I think one of the comments said that in Java there is no operator[] and the function is called put)? My point is why doesn't map operator[] work like a vector? If I use operator[] on an out of range index on a vector I wouldn't like it to insert an element even if it was cheap because that probably mean an error in my code. My point is why isn't it the same thing with map. I mean, for me, using operator[] on a map would mean: i know this key already exist (for whatever reason, I just inserted it, I have redundancy somewhere, whatever). I think it would be more intuitive that way.

That said what are the advantage of doing the current behavior with operator[] (and only for that, I agree that a function with the current behavior should be there, just not operator[])? Maybe it give clearer code that way? I don't know.

Another answer was that it already existed that way so why not keep it but then, probably when they (the ones before stl) choose to implement it that way they found it provided an advantage or something? So my question is basically: why choose to implement it that way, meaning a somewhat lack of consistency with other operator[]. What benefit do it give?

13 Answers

map.insert(key, item); makes sure key is in the map but does not overwrite an existing value.

map.operator[key] = item; makes sure key is in the map and overwrites any existing value with item.

Both of these operations are important enough to warrant a single line of code. The designers probably picked which operation was more intuitive for operator[] and created a function call for the other.

I know this is old question but no one seems to have answered it well IMO. So far I haven't seen any mention of this:

The possibility of undefined behavior is to be avoided! If there is any reasonable behavior besides UB, then I imagine we should go with that.

std::vector/array exhibits undefined behavior with a bad operator[] index because there is really no reasonable option, since this is one of the fastest, most fundamental things you can do in c/c++, and it would be wrong to try to check anything. Checking is what at() is for.

std::*associative_container* has already done the work of finding where an indexed element would go, so it makes sense to create one there and return it. This is very useful behavior, and alternatives to operator[] are much less clean looking, but even if creating and inserting a new item is not what you wanted, or is not useful to you, it is still a much better result than undefined behavior.

I think operator[] is much preferred syntax for using an associative container, for readability, and to me this is very intuitive, and matches exactly the concept of operator[] for arrays: return a reference to the item at that position, to use or to assign to.

If my intuition for "what if there is nothing there" was only "undefined behavior", then I would be absolutely no worse off, since I would be doing all I could do avoid that, full stop.

Then one day I find out that I can insert an item with operator[]... life is just better.

If you want to read an element with some key from an std::map,
but you are unsure whether it exists,
and in case it doesn't, you don't want to insert it by accident,
but rather want to get an exception thrown,
but you also don't want to manually check map.find(key) != map.end() everytime you read an element,

just use map::at(key) (C++11)

https://www.cplusplus.com/reference/map/map/at/

Related