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

`try_emplace`

and `insert_or_assign`

misspecified**Section:** 24.4.4.4 [map.modifiers], 24.5.4.4 [unord.map.modifiers] **Status:** C++17
**Submitter:** Thomas Koeppe **Opened:** 2014-12-17 **Last modified:** 2017-07-30

**Priority: **2

**View all other** issues in [map.modifiers].

**View all issues with** C++17 status.

**Discussion:**

The specification of the `try_emplace`

and `insert_or_assign`

member functions in N4279
contains the following errors and omissions:

In

`insert_or_assign`

, each occurrence of`std::forward<Args>(args)...`

should be`std::forward<M>(obj)`

; this is was a mistake introduced in editing.In

`try_emplace`

, the construction of the`value_type`

is misspecified, which is a mistake that was introduced during the evolution from a one-parameter to a variadic form. As written,`value_type(k, std::forward<Args>(args)...)`

does not do the right thing; it can only be used with a single argument, which moreover must be convertible to a`mapped_type`

. The intention is to allow direct-initialization from an argument pack, and the correct constructor should be`value_type(piecewise_construct, forward_as_tuple(k), forward_as_tuple(std::forward<Args>(args)...)`

.Both

`try_emplace`

and`insert_or_assign`

are missing requirements on the argument types. Since the semantics of these functions are specified independent of other functions, they need to include their requirements.

*[2015-02, Cologne]*

This issue is related to 2469^{(i)}.

AM: The repeated references to "first and third forms" and "second and fourth forms" is a bit cumbersome.
Maybe split the four functions?

GR: We don't have precendent for "EmplaceConstructible from a, b, c". I don't like the ambiguity between code commas and
text commas.

TK: What's the danger?

GR: It's difficult to follow standardese.

AM: It seems fine with code commas. What's the problem?

GR: It will lead to difficulties when we use a similar construction that's not at the end of a sentence.

AM: That's premature generalization. DK: When that happens, let's look at this again.

AM: Clean up "if the map does contain"

TK: Can we call both containers "map"? DK/GR: yes.

TK will send updated wording to DK.

Conclusion: Update wording, then poll for tentatively ready

*[2015-03-26, Thomas provides improved wording]*

The approach is to split the descriptions of the various blocks of four functions into two blocks each so as to make the wording easier to follow.

This wording is relative to N4296.

Apply the following changes to section 24.4.4.4 [map.modifiers] p3:

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

Requires: For the first and third forms,`value_type`

shall be`EmplaceConstructible`

into map from`piecewise_construct`

,`forward_as_tuple(k)`

,`forward_as_tuple(forward<Args>(args)...)`

. For the second and fourth forms,`value_type`

shall be`EmplaceConstructible`

into map from`piecewise_construct`

,`forward_as_tuple(move(k))`

,`forward_as_tuple(forward<Args>(args)...)`

.-3-

Effects:~~If the key~~If the map does already contain an element whose key is equivalent to`k`

already exists in the map, there is no effect. Otherwise, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the map whose key is equivalent to`k`

`k`

, there is no effect. Otherwise for the first and third forms inserts a`value_type`

object`t`

constructed with`piecewise_construct`

,`forward_as_tuple(k)`

,`forward_as_tuple(forward<Args>(args)...)`

, for the second and fourth forms inserts a`value_type`

object`t`

constructed with`piecewise_construct`

,`forward_as_tuple(move(k))`

,`forward_as_tuple(forward<Args>(args)...)`

.-?-

Returns: In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.Apply the following changes to section 24.4.4.4 [map.modifiers] p5:

template <class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template <class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template <class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);-?-

Requires:`is_assignable<mapped_type&, M&&>::value`

shall be true. For the first and third forms,`value_type`

shall be`EmplaceConstructible`

into map from`k`

,`forward<M>(obj)`

. For the second and fourth forms,`value_type`

shall be`EmplaceConstructible`

into map from`move(k), forward<M>(obj)`

.-5-

Effects:~~If the key~~If the map does already contain an element whose key is equivalent to`k`

does not exist in the map, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. If the key already exists,`std::forward<M>(obj)`

is assigned to the`mapped_type`

corresponding to the key. In the first two overloads, the`bool`

component of the returned value is true if and only if the insertion took place. The returned iterator points to the element that was inserted or updated`k`

,`forward<M>(obj)`

is assigned to the`mapped_type`

corresponding to the key. Otherwise the first and third forms inserts a`value_type`

object`t`

constructed with`k`

,`forward<M>(obj)`

, the second and fourth forms inserts a`value_type`

object`t`

constructed with`move(k), forward<M>(obj)`

.-?-

Returns: In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the map whose key is equivalent to`k`

.Apply the following changes to section 24.5.4.4 [unord.map.modifiers] p5:

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

Requires: For the first and third forms,`value_type`

shall be`EmplaceConstructible`

into unordered_map from`piecewise_construct`

,`forward_as_tuple(k)`

,`forward_as_tuple(forward<Args>(args)...)`

. For the second and fourth forms,`value_type`

shall be`EmplaceConstructible`

into unordered_map from`piecewise_construct`

,`forward_as_tuple(move(k))`

,`forward_as_tuple(forward<Args>(args)...)`

.-5-

Effects:~~If the key~~If the unordered_map does already contain an element whose key is equivalent to`k`

already exists in the map, there is no effect. Otherwise, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the map whose key is equivalent to`k`

`k`

, there is no effect. Otherwise for the first and third forms inserts a`value_type`

object`t`

constructed with`piecewise_construct`

,`forward_as_tuple(k)`

,`forward_as_tuple(forward<Args>(args)...)`

, for the second and fourth forms inserts a`value_type`

object`t`

constructed with`piecewise_construct`

,`forward_as_tuple(move(k))`

,`forward_as_tuple(forward<Args>(args)...)`

.-?-

Returns: In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the unordered_map whose key is equivalent to`k`

.Apply the following changes to section 24.5.4.4 [unord.map.modifiers] p7:

template <class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template <class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template <class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);-?-

Requires:`is_assignable<mapped_type&, M&&>::value`

shall be true. For the first and third forms,`value_type`

shall be`EmplaceConstructible`

into unordered_map from`k`

,`forward<M>(obj)`

. For the second and fourth forms,`value_type`

shall be`EmplaceConstructible`

into unordered_map from`move(k), forward<M>(obj)`

.-7-

Effects:~~If the key~~If the unordered_map does already contain an element whose key is equivalent to`k`

does not exist in the map, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. If the key already exists,`std::forward<M>(obj)`

is assigned to the`mapped_type`

corresponding to the key. In the first two overloads, the`bool`

component of the returned value is true if and only if the insertion took place. The returned iterator points to the element that was inserted or updated`k`

,`forward<M>(obj)`

is assigned to the`mapped_type`

corresponding to the key. Otherwise the first and third forms inserts a`value_type`

object`t`

constructed with`k`

,`forward<M>(obj)`

, the second and fourth forms inserts a`value_type`

object`t`

constructed with`move(k), forward<M>(obj)`

.-?-

Returns: In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the unordered_map whose key is equivalent to`k`

.

*[2015-05, Lenexa]*

STL: existing wording is horrible, this is Thomas' wording and his issue

STL: already implemented the piecewise part

MC: ok with changes

STL: changes are mechanical

STL: believe this is P1, it must be fixed, we have wording

PJP: functions are sensible

STL: has been implemented

MC: consensus is to move to ready

**Proposed resolution:**

This wording is relative to N4296.

Apply the following changes to 24.4.4.4 [map.modifiers] p3+p4:

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

~~template <class... Args> pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);~~template <class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);~~template <class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);~~-?-

*Requires*:`value_type`

shall be`EmplaceConstructible`

into`map`

from`piecewise_construct, forward_as_tuple(k), forward_as_tuple(forward<Args>(args)...)`

.-3-

*Effects*:~~If the key~~If the map already contains an element whose key is equivalent to`k`

already exists in the map, there is no effect. Otherwise, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the map whose key is equivalent to`k`

`k`

, there is no effect. Otherwise inserts an object of type`value_type`

constructed with`piecewise_construct, forward_as_tuple(k), forward_as_tuple(forward<Args>(args)...)`

.-?-

*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-4-

*Complexity*: The same as`emplace`

and`emplace_hint`

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

-?-

*Requires*:`value_type`

shall be`EmplaceConstructible`

into`map`

from`piecewise_construct, forward_as_tuple(move(k)), forward_as_tuple(forward<Args>(args)...)`

.-?-

*Effects*: If the map already contains an element whose key is equivalent to`k`

, there is no effect. Otherwise inserts an object of type`value_type`

constructed with`piecewise_construct, forward_as_tuple(move(k)), forward_as_tuple(forward<Args>(args)...)`

.-?-

*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-?-

*Complexity*: The same as`emplace`

and`emplace_hint`

, respectively.Apply the following changes to 24.4.4.4 [map.modifiers] p5+p6:

template <class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);

~~template <class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);~~template <class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);~~template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);~~-?-

*Requires*:`is_assignable<mapped_type&, M&&>::value`

shall be`true`

.`value_type`

shall be`EmplaceConstructible`

into`map`

from`k, forward<M>(obj)`

.-5-

*Effects*:~~If the key~~If the map already contains an element`k`

does not exist in the map, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. If the key already exists,`std::forward<M>(obj)`

is assigned to the`mapped_type`

corresponding to the key. In the first two overloads, the`bool`

component of the returned value is true if and only if the insertion took place. The returned iterator points to the element that was inserted or updated`e`

whose key is equivalent to`k`

, assigns`forward<M>(obj)`

to`e.second`

. Otherwise inserts an object of type`value_type`

constructed with`k, forward<M>(obj)`

.-?-

*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-6-

*Complexity*: The same as`emplace`

and`emplace_hint`

, respectively.template <class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);

-?-

*Requires*:`is_assignable<mapped_type&, M&&>::value`

shall be`true`

.`value_type`

shall be`EmplaceConstructible`

into`map`

from`move(k), forward<M>(obj)`

.-?-

*Effects*: If the map already contains an element`e`

whose key is equivalent to`k`

, assigns`forward<M>(obj)`

to`e.second`

. Otherwise inserts an object of type`value_type`

constructed with`move(k), forward<M>(obj)`

.*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-?-

*Complexity*: The same as`emplace`

and`emplace_hint`

, respectively.Apply the following changes to 24.5.4.4 [unord.map.modifiers] p5+p6:

~~template <class... Args> pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);~~template <class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);~~template <class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);~~-?-

*Requires*:`value_type`

shall be`EmplaceConstructible`

into`unordered_map`

from`piecewise_construct, forward_as_tuple(k), forward_as_tuple(forward<Args>(args)...)`

.-5-

*Effects*:`k`

already exists in the map, there is no effect. Otherwise, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. In the first two overloads, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the element of the map whose key is equivalent to`k`

`k`

, there is no effect. Otherwise inserts an object of type`value_type`

constructed with`piecewise_construct, forward_as_tuple(k), forward_as_tuple(forward<Args>(args)...)`

.*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-6-

*Complexity*: The same as`emplace`

and`emplace_hint`

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

-?-

*Requires*:`value_type`

shall be`EmplaceConstructible`

into`unordered_map`

from`piecewise_construct, forward_as_tuple(move(k)), forward_as_tuple(forward<Args>(args)...)`

.-?-

*Effects*: If the map already contains an element whose key is equivalent to`k`

, there is no effect. Otherwise inserts an object of type`value_type`

constructed with`piecewise_construct, forward_as_tuple(move(k)), forward_as_tuple(forward<Args>(args)...)`

.*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-?-

*Complexity*: The same as`emplace`

and`emplace_hint`

, respectively.Apply the following changes to 24.5.4.4 [unord.map.modifiers] p7+p8:

~~template <class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);~~template <class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);~~template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);~~-?-

*Requires*:`is_assignable<mapped_type&, M&&>::value`

shall be`true`

.`value_type`

shall be`EmplaceConstructible`

into`unordered_map`

from`k, forward<M>(obj)`

.-7-

*Effects*:`k`

does not exist in the map, inserts an element into the map. In the first and third forms, the element is constructed from the arguments as`value_type(k, std::forward<Args>(args)...)`

. In the second and fourth forms, the element is constructed from the arguments as`value_type(std::move(k), std::forward<Args>(args)...)`

. If the key already exists,`std::forward<M>(obj)`

is assigned to the`mapped_type`

corresponding to the key. In the first two overloads, the`bool`

component of the returned value is true if and only if the insertion took place. The returned iterator points to the element that was inserted or updated`e`

whose key is equivalent to`k`

, assigns`forward<M>(obj)`

to`e.second`

. Otherwise inserts an object of type`value_type`

constructed with`k, forward<M>(obj)`

.*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-8-

*Complexity*: The same as`emplace`

and`emplace_hint`

, respectively.template <class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template <class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);

-?-

*Requires*:`is_assignable<mapped_type&, M&&>::value`

shall be`true`

.`value_type`

shall be`EmplaceConstructible`

into`unordered_map`

from`move(k), forward<M>(obj)`

.-?-

*Effects*: If the map already contains an element`e`

whose key is equivalent to`k`

, assigns`forward<M>(obj)`

to`e.second`

. Otherwise inserts an object of type`value_type`

constructed with`move(k), forward<M>(obj)`

.*Returns*: In the first overload, the`bool`

component of the returned pair is`true`

if and only if the insertion took place. The returned iterator points to the map element whose key is equivalent to`k`

.-?-

*Complexity*: The same as`emplace`

and`emplace_hint`

, respectively.