What happens if I read a map's value where the key does not exist?

C++Map

C++ Problem Overview


map<string, string> dada;
dada["dummy"] = "papy";
cout << dada["pootoo"];

I'm puzzled because I don't know if it's considered undefined behaviour or not, how to know when I request a key which does not exist, do I just use find instead ?

C++ Solutions


Solution 1 - C++

The map::operator[] searches the data structure for a value corresponding to the given key, and returns a reference to it.

If it can't find one it transparently creates a default constructed element for it. (If you do not want this behaviour you can use the map::at function instead.)

You can get a full list of methods of std::map here:

http://en.cppreference.com/w/cpp/container/map

Here is the documentation of map::operator[] from the current C++ standard...

23.4.4.3 Map Element Access
T& operator[](const key_type& x);
  1. Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.

  2. Requires: key_type shall be CopyConstructible and mapped_type shall be DefaultConstructible.

  3. Returns: A reference to the mapped_type corresponding to x in *this.

  4. Complexity: logarithmic.

T& operator[](key_type&& x);
  1. Effects: If there is no key equivalent to x in the map, inserts value_type(std::move(x), T()) into the map.

  2. Requires: mapped_type shall be DefaultConstructible.

  3. Returns: A reference to the mapped_type corresponding to x in *this.

  4. Complexity: logarithmic.

Solution 2 - C++

If you try to access a key value using index operator [], then 2 things can happen :

  1. The map contains this key. So it will return the corresponding key value.
  2. The map doesn't contain the key. In this case, it will automatically add a key to the map with null value.

"pootoo" key does't exist in your map. So it will automatically add this key with value = ""(empty string). And your program will print empty string.

Here map size will increase by 1.

To search a key you can use map_name.find(), which will return map_name.end() if the key doesn't exist. And no extra key will be added.

You can use [] operator when you want to set value for a key.

Solution 3 - C++

It's not undefined behavior. If operator [] doesn't find a value for the provided key, it inserts one at that position.

Solution 4 - C++

For operator[], if you try to access a value for a key that doesn't exist, a new value object that has been default constructed will be put into the map and it's reference returned.

Solution 5 - C++

The operator[] for map returns a non-const reference and you can assign using that in the way you've shown on your second line. Accessing in this way will create a default contructed element of value type.

If you want to find a find an element, a better way is

iterator find ( const key_type& x )

(or the const alternative) which will return an iterator equal to <map>.end() if it doesn't find the key, or if you just want to know if it's in the collection you can use

size_type count ( const key_type& x ) const

which will always return either 1 or 0 for a map since keys are unique.

Solution 6 - C++

If operator [] doesn't find a value for the provided key, it inserts one at that position.

But you should note that if you visit a not exist key and invoke it's member function, like mapKV[not_exist_key].member_fun().The program may crash.

Let me give an example, test class as below:

struct MapValue{
    int val;
    
    MapValue(int i=0){
        cout<<"ctor: "<<i<<endl; val = i;
    }

    ~MapValue(){
        cout<<"dtor: "<<val<<endl;
    }

    friend ostream& operator<<(std::ostream& out, const MapValue& mv){
        cout<<"MapValue: "<<mv.val<<endl;
    }

    string toString(){
        cout<<"MapValue: "<<val<<endl;
    }
};

Test code:

cout<<"-------create map<int, MapValue>-------"<<endl;

map<int, MapValue> idName{{1, MapValue(1)}, {2, MapValue(2)}};

cout<<"-----cout key[2]-----"<<endl;
cout<<idName[2]<<endl;

cout<<"-----cout key[5]-----"<<endl;
cout<<idName[5]<<endl;

cout<<"------- runs here means, does't crash-------"<<endl;

Output as below:

-------create map<int, MapValue>-------
ctor: 1
ctor: 2
dtor: 2
dtor: 1
dtor: 2
dtor: 1
-----cout key[2]-----
MapValue: 2

-----cout key[5]-----
ctor: 0
MapValue: 0

-------runs here means, does't crash-------
dtor: 0
dtor: 2
dtor: 1

We can see that: idName[5] invoke map construct {5, MapValue(0)} to insert to idName.

But if, you invoke member function by idName[5], then the program crashes :

cout<<"-------create map<int, MapValue>-------"<<endl;

map<int, MapValue> idName{{1, MapValue(1)}, {2, MapValue(2)}};


idName[5].toString();  // get crash here.


cout<<"------- runs here means, doesn't crash-------"<<endl;

Solution 7 - C++

please have a look at the out_of_range exception: http://www.cplusplus.com/reference/stdexcept/out_of_range/

this is what map::at and map::operator[] will throw if key does not exist. You can catch it the same way as the vector example in the link.

You can also use: http://www.cplusplus.com/reference/map/map/find/

And check against map::end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjokoonView Question on Stackoverflow
Solution 1 - C++Andrew TomazosView Answer on Stackoverflow
Solution 2 - C++Ali AkberView Answer on Stackoverflow
Solution 3 - C++Luchian GrigoreView Answer on Stackoverflow
Solution 4 - C++Michael KohneView Answer on Stackoverflow
Solution 5 - C++Component 10View Answer on Stackoverflow
Solution 6 - C++JayhelloView Answer on Stackoverflow
Solution 7 - C++user2346536View Answer on Stackoverflow