Issue When Using [] Operator To Access Elements Of A map

I had the habit of using [] operator to access elements of a map. For example, if we have

map<int, int> myMap, to get the element corresponding to 10, we can use

myMap[10]. This is perfectly ok as long as we have an element corresponding to 10. But, what if we don’t have one corresponding to 10?

There lies the problem. In this situation, myMap is already added with a default elemnent corresponding to 10 ([] operator provides no facility to validate if an element exists) and in the above situation, it is 0. As a result, a new (unwanted) element is added to the map (we can see that the size of map has been incremented by 1).

So a better alternative is to use the find() function associated with map. It does not add an element and if the element is not found, it will return map::end().

The example given below illustrates this.

#include

#include
using namespace std;

int main()
{
   map myMap;
   cout << "Size of myMap = " << myMap.size() << '\n';   // will be 0    int content = myMap[0];    // Now the size of myMap will be 1 as we used the [] operator    // and the value of content = myMap[0] = 0    cout << "Size of myMap = " << myMap.size() << '\n';       // Instead, if we use find, the size will not change.    // It will look for the element only.    map::const_iterator it = myMap.find(1);    // Here the element has not been found. So it = myMap.end()    cout << "Size of myMap = " << myMap.size() << '\n';   // will be 1 only     return 0;    } [/sourcecode] So the best practice is to avoid [] operator for accessing individual elements of a map.

Advertisements

11 Responses to Issue When Using [] Operator To Access Elements Of A map

  1. pizer says:

    Since operator[] is supposed to return a reference there is only one other choice of action in case the key doesn’t exist: throwing an exception. But operator[] the way it is currently defined is useful, too. I would still use it for writing to the map. It’s just convenient.

    map<string,string> phonebook;
    phonebook[“Alf”] = “555 8531”;

  2. cppkid says:

    Thanks for the comment.
    [], indeed, is convenient to insert into map. Still, for the same, isn’t the insert function more elegant as the other one ([]) has to return a (non-const) reference?

    Please keep watching the blog.

  3. pizer says:

    Why would insert be more elegant? I find insert for maps annoying because you have to create a pair of the right type:

    typedef std::map map_t;
    phonebook.insert(map_t::value_type(“Alf”,”555 8531″));

    Also, the [] approach might be faster in some cases because it might not involve much copying. Any decently designed “value class” uses the “copy & swap”-idiom for assignment which is known to avoid unneccesary copying when the compiler performs return-value-optimization, see here.

    -P

  4. pizer says:

    make that
    typedef std::map<std::string,std::string> map_t

    I forgot that wordpress likes to swallow angle brackets. 😉

    -P

  5. cppkid says:

    [] operator does not provide any facility to find if an element corresponding to a key exists or not. For example,

    map<int, int> myMap;
    myMap[10] = 20;
    myMap[10] = 40;
    

    will leave a value 40 corresponding to the key 10. However, with insert() function, we have the option to see if the key already exists or not. It returns a pair. The iterator points to the element with the key specified and the bool shows if the operation was successful or not (If the element already existed, the insertion won’t take place and the return value will be false).

    For example,

    typedef map<int, int>::iterator MyMapIterator;
    typedef pair<int, int> MyPair;
    // Add a pair (10, 20) to the map
    myMap[10] = 20;
    // Try to add the pair (10, 30) using insert
     pair P = myMap.insert(MyPair(10, 30));
    

    Now the value of P.second wil be false as the insertion failed and P.first will point to the element (10, 20).

    If we had simply used the operator [] for insertion, we would have ended up with an element (10, 30).

    However, when there is the need of overwriting the existing elements or when you are absolutely sure that you are not going to overwrite any of the values, you can use the operator []. It is easy to use and more readable.

  6. alatiagFiny says:

    Maaaan, you know there is such thing in the web like search engine, http://google.com if you don’t, go there to understand why this post is bullshit

  7. cppkid says:

    Dear alatiagFiny,
    thanks for your comment.
    FYI, I know there is search engine and I’ve been using many of them.
    Then, if you feel this post is a bullshit, you have to tell me why, rather than asking google to answer for you. I’ve written what I’ve experienced.
    I hope you got my point.

  8. spr says:

    Using “[]=” operator to insert into a map creates a default value associated with key in the container, and then uses the assignment operatror to get the contents of val into the newly created
    value.
    So, most probably insert operator should work faster than “[]”.

  9. pizer says:

    It really depends on the implementation of the mapped_type.

    There are situations where “[]=” might be faster than the insert. In the “[]=”-version mapped_type::operator= is used that might accept its parameter by value and use copy-and-swap. Together with a compiler employing RVO which eliminates this copy in case where the right hand side of the assignment was an rvalue you end up with an assignment that doesn’t copy the object at all. It is constructed into a temporary and swapped with the default-constructed value of the map.

    This should apply to many (well-designed) resource handle classes where copying is expensive and swapping is cheap.

  10. Hawkeye Parker says:

    Thanks for the post. I just fixed a legacy bug involving implicit add via []. The code was not using find(), but was simply checking for values by []. As a side affect, some unwanted string keys were added to the map, and those went out of scope while the static map (and the rest of its data) remained. So, the next time we tried to access the map, it had a bunch of garbage in it, and this garbage was “randomly” spread around the map. Hard to reproduce, and hard to fix, unless you’re familiar with std::map. I wouldn’t have (and didn’t) expect a get operation to modify the underlying map.

    My take away isn’t about how to initialize a std::map, but to never access one via [] without .find()’ing first, especially if you’re in static scope. Freaky.

  11. source says:

    If you dont mind, exactly where do you host your blog? I am searching for a good quality web host and your website appears to be extremely fast and up almost all the time

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: