Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does std::unordered_map operator[] do zero-initialization for non-exisiting key?

According to cppreference.com, std::map::operator[] for non-existing value does zero-initialization.

However, the same site does not mention zero-initialization for std::unordered_map::operator[], except it does have an example which relies on this.

Of course this is just a reference site, not the standard. So, is the code below ok or not?

#include <unordered_map> int main() {     std::unordered_map<int, int> map;     return map[42];     // is this guaranteed to return 0? } 
like image 431
hyde Avatar asked Dec 05 '19 09:12

hyde


People also ask

Is unordered_map initialized to 0?

The value object is value-initialized, not zero-initialized.

What does an unordered_map return if the key does not exist?

the unordered map will try to default initialize mystruct with the key 'x' and assign to my struct. if u wanna avoid this, use . at(key) if it doesn't exist it will throw an out_of_range exception , which you can catch it and handle.

What is the default value in unordered_map?

As you might expect, an unordered_map returns the default value for the value type if the key is not found in the map (count[5] will return 0 in this case).


2 Answers

On the site you linked it says:

When the default allocator is used, this results in the key being copy constructed from key and the mapped value being value-initialized.

So the int is value-initialized:

The effects of value initialization are:

[...]

4) otherwise, the object is zero-initialized

This is why the result is 0.

like image 117
Blaze Avatar answered Sep 24 '22 08:09

Blaze


Depending on which overload we're talking about, std::unordered_map::operator[] is equivalent to [unord.map.elem]

T& operator[](const key_type& k) {     return try_­emplace(k).first->second; } 

(the overload taking an rvalue-reference just moves k into try_emplace and is otherwise identical)

If an element exists under key k in the map, then try_emplace returns an iterator to that element and false. Otherwise, try_emplace inserts a new element under the key k, and returns an iterator to that and true [unord.map.modifiers]:

template <class... Args> pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); 

Interesting for us is the case of there being no element yet [unord.map.modifiers]/6:

Otherwise inserts an object of type value_­type constructed with piecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...)

(the overload taking an rvalue-reference just moves k into forward_­as_­tuple and, again, is otherwise identical)

Since value_type is a pair<const Key, T> [unord.map.overview]/2, this tells us that the new map element will be constructed as:

pair<const Key, T>(piecewise_­construct, forward_­as_­tuple(k), forward_­as_­tuple(std​::​forward<Args>(args)...)); 

Since args is empty when coming from operator[], this boils down to our new value being constructed as a member of the pair from no arguments [pairs.pair]/14 which is direct initialization [class.base.init]/7 of a value of type T using () as initializer which boils down to value initialization [dcl.init]/17.4. Value initialization of an int is zero initialization [dcl.init]/8. And zero initialization of an int naturally initializes that int to 0 [dcl.init]/6.

So yes, your code is guaranteed to return 0…

like image 41
Michael Kenzel Avatar answered Sep 23 '22 08:09

Michael Kenzel