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.

### 2904. Make `variant` move-assignment more exception safe

Section: 20.7.3.4 [variant.assign] Status: C++17 Submitter: United States Opened: 2017-02-03 Last modified: 2020-09-06

Priority: Not Prioritized

View all other issues in [variant.assign].

View all issues with C++17 status.

Discussion:

Addresses US 119 and CH 7

The copy-assignment operator is very careful to not destroy the contained element until after a temporary has been constructed, which can be safely moved from.

This makes the `valueless_by_exception` state extremely rare, by design.

However, the same care and attention is not paid to the move-assignment operator, nor the assignment-from-deduced-value assignment template. This concern should be similarly important in these cases, especially the latter.

Proposed change: —

[2017-03-02, Kona, Casey comments and suggests wording]

The wording below has been developed with much input from Tomasz.

[Kona 2017-03-02]

Accepted as Immediate to resolve NB comment.

Proposed resolution:

This wording is relative to N4640.

1. Modify 20.7.3.4 [variant.assign] as indicated:

   > [Drafting note: Presentation of para 9 immediately below has been split into individual bullets.]

   >     variant& operator=(const variant& rhs);

   > > Let `j` be `rhs.index()`.

   > > -1- Effects:

   > > 1. (1.1) — If neither `*this` nor `rhs` holds a value, there is no effect. Otherwise,

   > > 2. (1.2) — if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value. Otherwise,

   > > 3. (1.3) — if `index() == jrhs.index()`, assigns the value contained in `rhs` to the value contained in `*this`. Otherwise,

   > > 4. (1.?) — if `is_nothrow_copy_constructible_v<Tj> || !is_nothrow_move_constructible_v<Tj>` is `true`, equivalent to `emplace<j>(get<j>(rhs))`. Otherwise,

   > > 5. (1.4) — equivalent to `operator=(variant(rhs))`copies the value contained in `rhs` to a temporary, then destroys any value contained in `*this`. Sets `*this` to hold the same alternative index as `rhs` and initializes the value contained in `*this` as if direct-non-list-initializing an object of type `Tj` with `std::forward<Tj>(TMP)`, with `TMP` being the temporary and `j` being `rhs.index()`.

   > > -2- Returns: `*this.`

   > > -3- Postconditions: `index() == rhs.index()`.

   > > -4- Remarks: This function shall not participate in overload resolution unless `is_copy_constructible_v<Ti> && is_move_constructible_v<Ti> && is_copy_assignable_v<Ti>` is `true` for all `i`.

   > > 1. (4.1) — If an exception is thrown during the call […]

   > > 2. (4.2) — If an exception is thrown during the call […]

   > > 3. (4.3) — If an exception is thrown during the call […]

   >     variant& operator=(variant&& rhs) noexcept(see below);

   > > Let `j` be `rhs.index()`.

   > > -5- Effects:

   > > 1. (5.1) — If neither `*this` nor `rhs` holds a value, there is no effect. Otherwise,

   > > 2. (5.2) — if `*this` holds a value but `rhs` does not, destroys the value contained in `*this` and sets `*this` to not hold a value. Otherwise,

   > > 3. (5.3) — if `index() == jrhs.index()`, assigns `get<j>(std::move(rhs))` to the value contained in `*this`, with `j` being `index()`. Otherwise,

   > > 4. (5.4) — equivalent to `emplace<j>(get<j>(std::move(rhs)))`destroys any value contained in `*this`. Sets `*this` to hold the same alternative index as `rhs` and initializes the value contained in `*this` as if direct-non-list-initializing an object of type `Tj` with `get<j>(std::move(rhs))` with `j` being `rhs.index()`.

   > > […]

   > […]

   >     template <class T> variant& operator=(T&& t) noexcept(see below);

   > > -8- […]

   > > -9- Effects:

   > > 1. (9.1) — If *this holds a `Tj`, assigns `std::forward<T>(t)` to the value contained in `*this`. Otherwise,

   > > 2. (9.?) — if `is_nothrow_constructible_v<Tj, T> || !is_nothrow_move_constructible_v<Tj>` is `true`, equivalent to `emplace<j>(std::forward<T>(t))`. Otherwise,

   > > 3. (9.3) — equivalent to `operator=(variant(std::forward<T>(t)))`destroys any value contained in `*this`, sets `*this` to hold the alternative type `Tj` as selected by the imaginary function overload resolution described above, and direct-initializes the contained value as if direct-non-list-initializing it with `std::forward<T>(t)`.