Search code examples
c++stlstdstdmaplanguage-design

Why is std::map::operator[] so counter-intuitive?


It seems to me 'evil' (in the C++ FAQ sense of the word), for an operator which is generally used to access a data structure to suddenly be defined to insert data into a data structure.

I guess the issue is 'what would be better'? This question is answered easily for certain types of mapped value; for example, if we map keys to pointers, you might really like operator[] to return nullptr for a non-existent key, but that clearly doesn't work for other types.

It could throw an exception on non-existent key, or even default construct a temporary and return that without adding it to the map. What is the good reason for turning [] from read semantics to write semantics for this container type?


Solution

  • The basic problem is that there is no syntactic way to reliably distinguish:

    dosomething(collection[foo]);
    

    from

    collection[foo] = something;
    

    in the operator's definition. Because it may appear in either location, the class makes sure that it can handle both, providing a default to overwrite, if necessary. If you find this to be unconscionable, then you need to avoid std::map::operator[] altogether.

    Another reason for this is there must be some defined behavior for when the key is not in the list. Since operator[] must return a value (either LValue or RValue), then it cannot return a null pointer, past-the-end iterator, or any other sentinel value. The only remaining option would be to raise an exception. The STL doesn't raise very many exceptions, because it is intended to be used even in cases where exceptions are not. Some other behavior must be chosen, and this is the result.

    The best way around this is to use a member function of std::map that doesn't have this behavior. That would be map::find(), which returns map::end if the key is not found.