std::map<string,int> default initialization of value [duplicate] - c++

This question already has answers here:
std::map default value for build-in type
(4 answers)
Closed 6 years ago.
This piece of code seems work well, with default value for they value_type (int) as 0; does it work for all cases?
std::map<std::string,int> w;
for (const auto& t: str)
w[t]++;
What about double?
map? default 0.0?

Yes, this code would work for any type of the key, including double. The reason this works is that the non-const operator [] returns a reference to the value at the key, not a copy of that value. It is that reference to which the ++ operator gets applied.
The code fragment that you show works as follows:
For each key t of type string in the str container,
The map w is searched for the given key
Since the entry is not there, a new one gets inserted into the map
Since the key of the entry is known, but the value is not, a default (value-initialized, e.i. 0 for int) object for the value gets created
A reference to the newly created object (in this case, int& initialized to zero) is returned to the caller
The ++ operator is applied to the reference returned from the [], which changes 0 to 1 (or 0.0 to 1.0, etc.)

Yes. When you use the []-operator on a map and no element with the desired key exists, a new element is inserted which is value-initialized. For an integer, this means initialized to zero.

does it work for all cases?
For all cases, a new key will be associated with a value initialized to T().
When T is a built-in or Plain Old Data type, such as int or double, that evaluates to zero.
When T is a class, the map will attempt to call the empty constructor.

Related

C++ unordered_map/map [] operator default initialization value [duplicate]

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.
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.
This will default-construct a new instance of value. For integers, the default construction is 0, so this works as intended.
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.
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.

should I use ++wordCount[key] for unordered_map<string, int> when key doesn't exist in wordCount?

See code below:
unordered_map<string, int> wordCount;
for(string word: words)
++wordCount[word];
Question:
Does it right here for using ++wordCount[word]; when word does not exist in wordCount? I always saw someone using like this, but I am not really sure about it.
Explanation here said:
If k does not match the key of any element in the container, the
function inserts a new element with that key and returns a reference
to its mapped value. Notice that this always increases the container
size by one, even if no mapped value is assigned to the element (the
element is constructed using its default constructor).
and I know that int is uninitialized in default. So, I think it's not safe to increase map value directly like this, is that right?
Quoting cppreference's docs on operator[]
When the default allocator is used, this results in the key being copy constructed from key and the mapped value being value-initialized.
The keyword is value-initialized. An int that's value-initialized is zero.
Here's the official reference (C++14, 23.5.4.3 paragraph 2, [unord.map.elem]):
Effects: If the unordered_map does not already contain an element whose key is equivalent to k, the first operator inserts the value value_type(k, mapped_type()) and the second operator inserts the
value value_type(std::move(k), mapped_type()).
Notice the mapped_type(), which for mapped_type = int would construct a zero-valued int.

C++ map :first call to map[a], how to initialize map[a].second to zero?

When I use operator[] to call map[a]; while there is no key a in the map will the second element in map[a]be initialized to zero? If not, are there any good way to do it? I'm currently using if (map.find(a)) map[a]++; else map[a] = 1;. I want to use a map that is initialized to 0 and calling only map[a]++; and map[a]--;
Assuming a map with a type such as std::map<int, int> m, then a call to m[a] for a non-existent key a will insert an element with key a and value int(), that is, 0. So you can safely simplify your code to
std::map<int, int> m
....
m[a]++; // OK
In the general case, the new element insertion inserts a value initialized key object to the map. For built-in arithmetic types such as int, value initialization means zero initialization.
Assuming a std::map, if operator[] creates a new element (i.e. for a key that doesn't previously exist, then the mapped value is value initialised.
For basic types like int, that means a value of zero. For class/class types, it means default construction.

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

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;
}
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.
It's guaranteed that you'll get 1 even with the shorter snippet.
The key is that operator[] has to create the element in the map before returning a reference to it, so by the time you got to += the element already exists, and, if it had to be created now, with value zero (since elements of a map are value-initialized).
(incidentally, this is the reason why, when you use an std::map with a class type as value, it has to have a default constructor if you want to use operator[], even if you assign an object to it immediately)
Yes this is fine, new entries default to value value_type() which is 0 for int.
It still does +=, but 0 += 1 gives 1.
Map is lazily initialize with default constructor.(zero for all int)
So it 0 didn't exist in map, it would be initialized to contain value 0 and 1 would be added after += 1.
So you can use upper version of code without any worries.
Quoting Effect of calling map_obj[k] from cplusplus.com
If k matches the key of an element in the container, the function
returns a reference to its mapped value.
If k does not match the key of any element in the container, the
function inserts a new element with that key and returns a reference
to its mapped value. Notice that this always increases the container
size by one, even if no mapped value is assigned to the element (the
element is constructed using its default constructor).

Changing Assigned Value for STL Map [duplicate]

This question already has answers here:
std::map default value
(16 answers)
Closed 8 years ago.
As I create a std::map, all the indexes are initially pointing nowhere. As soon as I call them, they allocated and assigned the value 0.
For example:
map <int, int> x;
cout << x.[31233];
The output is 0.
However, I need that all the standard assigned value to be (-1) instead of 0.
How can I change this?
After this definition
map <int, int> x;
the map has no elements. To add an element with key 31233 and value -1 you can write
x[31233] = -1;
You can not do such a way that the default value would be -1 because according to the C++ Standard relative to the subscript operator
If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map
For type int int() zero initializes the corresponding object.
Otherwise you should use some insert method where you will explicitly specify an initial value.
Given a std::map<key_type, value_type>, a lookup for an arbitrary key through operator [](cont key_type&) will auto-insert value_type() for said-key if it wasn't present. In your case, value_type is int, and int() is zero-initialized, therefore zero is the result.
if you want to use a different default construct, you have options, the most extreme of which would be writing a custom allocator specialized for int and a construct member to us -1 for int value types (yuck). I think you may find it easier to simply:
std::map <int, int> x;
// load map with values...
int res = -1;
auto it = x.find(31233);
if (it != x.end())
res = x.second;
// use res here.
You should simply stop using the operator[] and instead use insert() when you intend to insert a new value. If you want to have a "default" value for things that have never been inserted, it may be better to write your own container which uses a map internally and exposes something like a get() method which returns the stored value from the map or -1 if not found. That way you avoid storing lots of -1 values that aren't needed.