Issue 2274: Does map::operator[] value-initialize or default-insert a missing element? (original) (raw)
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: 23.4.3.3 [map.access], 23.5.3.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 { using super = std::allocator;
template<typename Up, typename... Args> void construct(Up* p, Args&&... args) { super::construct(p, std::forward(args)...); }
template 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, Allocator1> map; cout << map["cat"];
N3960 is not very clear. 23.4.3.3 [map.access] in para 1 says:
"If there is no key equivalent to
xin the map, insertsvalue_type(x, T())into the map."
So, it requires value-initialization.
But para 2 says:
"
mapped_typeshall beDefaultInsertableinto*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(i), this wording would also address this issue.
[2015-02 Cologne]
Wait until 2464(i) and 2469(i) are in, which solve this.
[2015-05-06 Lenexa: This is resolved by 2469(i).]
Proposed resolution:
This wording is relative to N3691.
- Change 23.4.3.3 [map.access] p1+p5 as indicated:
T& operator[](const key_type& x);
-1- Effects: If there is no key equivalent to
xin the map, insertsinto the map a value withvalue_type(x, T())into the mapkey_typeinitialized using expressionxandmapped_typeinitialized by default-insertion.-2- Requires:
key_typeshall beCopyInsertableandmapped_typeshall beDefaultInsertableinto*this.[…]
T& operator[](key_type&& x);
-5- Effects: If there is no key equivalent to
xin the map, insertsinto the map a value withvalue_type(std::move(x), T())into the mapkey_typeinitialized using expressionstd::move(x)andmapped_typeinitialized by default-insertion.-6- Requires:
mapped_typeshall beDefaultInsertableinto*this. - Change 23.5.3.3 [unord.map.elem] p2 as indicated:
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);-1- Requires:
mapped_typeshall beDefaultInsertableinto*this. For the first operator,key_typeshall beCopyInsertableinto*this. For the second operator,key_typeshall beMoveConstructible.-2- Effects: If the
unordered_mapdoes not already contain an element whose key is equivalent tok, the first operator insertsthe valuea value withvalue_type(k, mapped_type())key_typeinitialized using expressionxandmapped_typeinitialized by default-insertionand the second operator insertsthe valuea value withvalue_type(std::move(k), mapped_type())key_typeinitialized using expressionstd::move(x)andmapped_typeinitialized by default-insertion.