Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Safe to use += operator to create a new std::map entry using []?

Tags:

c++

map

Let's say I have a std::map<int, int>, would it be safe to do this?

std::map<int, int> m_map;
m_map[0] += 1;

If the key 0 didn't exist in the map when I do this, how would it know what value to add 1 to?

What I'm hoping is that std::map handles this by performing an = instead of += in the case where the value creates a new entry in the map. This would spare me from having to do:

std::map<int, int>::iterator it = m_map.find(0);
if(it != m_map.end()) {
    it->second += 1;
}
else {
    m_map[0] = 1;
}
like image 534
Nic Foster Avatar asked Apr 28 '14 05:04

Nic Foster


People also ask

How do you put a STD on a map?

If you want to insert element in std::map - use insert() function, and if you want to find element (by key) and assign some to it - use operator[].

What is the complexity of std::map :: insert () method?

Time complexity: k*log(n) where n is size of map, k is no. of elements inserted.

Can we assign a map to another map in C++?

Starting with C++17, we can use the std::map::merge member function to copy content from a map to another map. This is the recommended way to copy the contents of a map to an existing map in C++17 and above. That's all about copying entries of a map to another map in C++.


1 Answers

An element inserted into a map on invoke of operator[] due to a previously-unmapped key is value-initialized. If you've not seen that syntax, consider the special meaning the () has in the following code snippet. The parens are important. They introduce an different trip down the initialization tree than default-initialization. Both are important; both are laid out by the language standard.

int i = int();

As it turns out, value initialization for scalars (including pointers) eventually succumbs to zero-initialization. Though odd looking, the prior snippet value-initializes an instance of int, which becomes zero-initialization since int is a scalar, then copies it to i. (To be fair, there will almost certainly be some eliding, but the fundamentals are as-presented).

Regardless, due to that feature, you can rest assured when you do this:

m_map[0] += 1;

or even this:

++m_map[0];

if the index wasn't mapped prior, a value-initialized element is added, which will zero-initialize for scalars, which means you'll officially starting out with zero.

It is worth mentioning that a similar activity happens for any type with an implicitly declared constructor. Whether trivial or not, something interesting happens.

struct S { int a; int b; };
std::map<int, S> mymap;

++mymap[0].a;

Is the a member mapped to 0 in our container reliably 1 after the above executes? Yes, it is. Further, consider this:

struct S { int a; std::string str; };
std::map<int, S> mymap;

++mymap[0].a;

Now S has a non-trivial implicit constructor (it has to, as it must construct str). But is the a member mapped to 0 in our container still reliably zero-initialized (and therefore 1 after the above line)? Yes, it is.

If curious about the referenced different paths of initialization, see this question and answer. Or review the C++11 standard, particularly C++11 § 8.5 Initializers, (p5,p7,p10). It is worth the read.

like image 95
WhozCraig Avatar answered Sep 22 '22 08:09

WhozCraig