This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Resolved status.

### 2274. Does `map::operator[]` value-initialize or default-insert a missing element?

Section: 24.4.4.3 [map.access], 24.5.4.3 [unord.map.elem] Status: Resolved Submitter: Andrzej Krzemieński Opened: 2013-07-16 Last modified: 2015-10-22

Priority: 3

View all other issues in [map.access].

View all issues with Resolved status.

Discussion:

Suppose that I provide a custom allocator for type `int`, that renders value 1 rather than 0 in default-insertion:

>     struct Allocator1 : std::allocator<int>
>     {
>       using super = std::allocator<int>;

>       template<typename Up, typename... Args>
>       void construct(Up* p, Args&&... args)
>       { super::construct(p, std::forward<Args>(args)...); }

>       template<typename Up>
>       void construct(Up* p)
>       { ::new((void*)p) Up(1); }
>     };

Now, if I use this allocator with `std::map`, and I use `operator[]` to access a not-yet-existent value, what value of the `mapped_type` should be created? 0 (value-initialization) or 1 (default-insertion):

>     map<string, int, less<string>, Allocator1> map;
>     cout << map["cat"];

N3960 is not very clear. 24.4.4.3 [map.access] in para 1 says:

> "If there is no key equivalent to `x` in the map, inserts `value_type(x, T())` into the map."

So, it requires value-initialization.

But para 2 says:

> "`mapped_type` shall be `DefaultInsertable` into `*this`."

This implies default-insertion, because if not, why the requirement. Also similar functions like `vector::resize` already require default-insertion wherever they put `DefaultInsertable` requirements.

Not to mention that default-insertion is more useful, because it allows custom allocators to "override" the default value of `mapped_type`.

[2013-09 Chicago]

Alisdair: Matters only for POD or trivial types

Marshall: issue might show up elsewhere other than `map<>`

Alisdair: initialize elements in any containers — by calling construct on allocator traits

Marshall: existing wording is clear

Alisdair: main concern is difference in wording, discusses default initialization

Nico: different requirement needed

Alisdair: gut is issue is NAD, brings up `DefaultInsertable` definition — discusses definition

Nico: why do we have the requirement?

Alisdair: other containers have this requirement

Marshall: this applies to many other containers

Nico: `deque<>` in particular

Alisdair: discusses allocator construct

Alisdair: wording raises concerns that aren't said in existing standard

Nico: sees no benefit to change

Marshall: leery of change

Alisdair: can be made clearer; might need to add note to `DefaultInsertable`; borderline editorial, comfortable without note, willing to wait until other issues arise. close issue as NAD

[2015-01-20: Tomasz Kamiński comments]

With the addition of the `try_emplace` method the behavior of the `operator[]` for the maps, may be defined as follows:

>     T& operator[](const key_type& x);

> > Effects: Equivalent to: `try_emplace(x).first->second`;

>     T& operator[](key_type&& x);

> > Effects: Equivalent to `try_emplace(std::move(x)).first->second`;

This would simplify the wording and also after resolution of the issue 2464, this wording would also address this issue.

[2015-02 Cologne]

Wait until 2464 and 2469 are in, which solve this.

[2015-05-06 Lenexa: This is resolved by 2469.]

Proposed resolution:

This wording is relative to N3691.

1. Change 24.4.4.3 [map.access] p1+p5 as indicated:

   >     T& operator[](const key_type& x);

   > > -1- Effects: If there is no key equivalent to `x` in the map, inserts `value_type(x, T())` into the map into the map a value with `key_type` initialized using expression `x` and `mapped_type` initialized by default-insertion.

   > > -2- Requires: `key_type` shall be `CopyInsertable` and `mapped_type` shall be `DefaultInsertable` into `*this`.

   > > […]

   >     T& operator[](key_type&& x);

   > > -5- Effects: If there is no key equivalent to `x` in the map, inserts `value_type(std::move(x), T())` into the map into the map a value with `key_type` initialized using expression `std::move(x)` and `mapped_type` initialized by default-insertion.

   > > -6- Requires: `mapped_type` shall be `DefaultInsertable` into `*this`.

2. Change 24.5.4.3 [unord.map.elem] p2 as indicated:

   >     mapped_type& operator[](const key_type& k);
   >     mapped_type& operator[](key_type&& k);

   > > -1- Requires: `mapped_type` shall be `DefaultInsertable` into `*this`. For the first operator, `key_type` shall be `CopyInsertable` into `*this`. For the second operator, `key_type` shall be `MoveConstructible`.

   > > -2- 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())`a value with `key_type` initialized using expression `x` and `mapped_type` initialized by default-insertion and the second operator inserts the value `value_type(std::move(k), mapped_type())`a value with `key_type` initialized using expression `std::move(x)` and `mapped_type` initialized by default-insertion.