45

I have a std::map like this:

map<wstring,int> Scores;

It stores names of players and scores. When someone gets a score I would simply do:

Scores[wstrPlayerName]++;

When there is no element in the map with the key wstrPlayerName it will create one, but does it initialize to zero or null before the increment or is it left undefined?

Should I test if the element exists every time before increment?

I just wondered because I thought primitive-type things are always undefined when created.

If I write something like:

int i;
i++;

The compiler warns me that i is undefined and when I run the program it is usually not zero.

Samuel Liew
  • 76,741
  • 107
  • 159
  • 260
Calmarius
  • 18,570
  • 18
  • 110
  • 157

4 Answers4

64

operator[] looks like this:

Value& map<Key, Value>::operator[](const Key& key);

If you call it with a key that's not yet in the map, it will default-construct a new instance of Value, put it in the map under key you passed in, and return a reference to it. In this case, you've got:

map<wstring,int> Scores;
Scores[wstrPlayerName]++;

Value here is int, and ints are default-constructed as 0, as if you initialized them with int(). Other primitive types are initialized similarly (e.g., double(), long(), bool(), etc.).

In the end, your code puts a new pair (wstrPlayerName, 0) in the map, then returns a reference to the int, which you then increment. So, there's no need to test if the element exists yet if you want things to start from 0.

Todd Gamblin
  • 58,354
  • 15
  • 89
  • 96
12

This will default-construct a new instance of value. For integers, the default construction is 0, so this works as intended.

hazzen
  • 17,128
  • 6
  • 41
  • 33
  • 1
    For integers, default *construction* is not really a thing. Default *initialization* leaves them uninitialized unless they are static. Maps *value*-initialize elements, which is why in the case of integers you get zero-initialization. – juanchopanza Dec 18 '16 at 22:44
5

You should not test if the item exists before incrementing it. The [] operator does exactly what you need it to do, as others have said.

But what if the default-constructed value wouldn't work for you? In your case the best way to find if the element already exists is to try to insert it. The insert member function for std::map returns a std::pair<iterator, bool>. Whether the insert succeeds or fails, the first element of the pair will point to the desired object (either your new one, or the one that was already present). You can then alter its value as you see fit.

Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
1

Check rules for initialization.

See section 4.9.5 Initialization of C++ Prog Lang or C++ std book. Depending on whether your variable is local, static, user-defined or const default initialization can happen.

In you case, int is called POD (Plain old Datatype). Any auto (created on heap / local variable) POD variable is not default initialized. Hence for you "i" above will not have value zero.

Always make an habit of initializing POD when defined in heap. You can even use int() to initialize value.

Ketan
  • 1,017
  • 8
  • 17
  • 3
    What you've said about PODs is correct, but the OP is asking about initialisation performed by std::map. Your answer doesn't apply and may be confusing to readers as it appears to contradict the other answers. – MatthewD Jul 23 '13 at 04:06
  • Alright, your answer is actually responding to @Calmarius's answer, which is now **below** (not **above**) as I'm reading it now. This really should be a comment there, not a separate answer. – MatthewD Jul 23 '13 at 04:09
  • As an answer to the question posted, this is completely wrong. – juanchopanza Dec 18 '16 at 22:43