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.

### 2464. `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:

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

2. 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)...)`.

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

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.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4296.

> 1. 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 `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` If the map does already contain an element whose key is equivalent to `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`.

> 2. 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 `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 If the map does already contain an element whose key is equivalent to `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`.

> 3. 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 `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` If the unordered_map does already contain an element whose key is equivalent to `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`.

> 4. 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 `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 If the unordered_map does already contain an element whose key is equivalent to `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.

1. 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 `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` 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(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.

2. 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 `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 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 `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.

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

   >     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 `unordered_map` from `piecewise_construct, forward_as_tuple(k), forward_as_tuple(forward<Args>(args)...)`.

   > > -5- Effects: If the key `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` 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(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.

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

   >     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 `unordered_map` from `k, forward<M>(obj)`.

   > > -7- Effects: If the key `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 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 `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.