Title
Does map::operator[] value-initialize or default-insert a missing element?
Status
resolved
Section
[map.access] [unord.map.elem]
Submitter
Andrzej Krzemieński

Created on 2013-07-16.00:00:00 last changed 108 months ago

Messages

Date: 2015-05-22.20:19:09

Proposed resolution:

This wording is relative to N3691.

  1. Change [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 mapinto 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 mapinto 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 [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.

Date: 2015-05-06.00:00:00

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

Date: 2015-03-29.20:41:17

[ 2015-02 Cologne ]

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

Date: 2015-01-20.00:00:00

[ 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.

Date: 2014-01-12.11:18:37

[ 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

Date: 2013-07-16.00:00:00

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. [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.

History
Date User Action Args
2015-05-22 20:19:09adminsetmessages: + msg7455
2015-05-22 20:19:09adminsetstatus: open -> resolved
2015-03-29 20:41:17adminsetmessages: + msg7275
2015-01-20 19:05:43adminsetmessages: + msg7227
2014-01-12 11:18:37adminsetmessages: + msg6766
2014-01-12 11:18:37adminsetstatus: new -> open
2013-08-18 21:00:24adminsetmessages: + msg6554
2013-07-16 00:00:00admincreate