3991. variant's move assignment should not be guaranteed to produce a valueless by exception state

Section: 22.6.3.4 [variant.assign] Status: New Submitter: Brian Bi Opened: 2023-08-29 Last modified: 2023-10-30

Priority: 3

View other active issues in [variant.assign].

View all other issues in [variant.assign].

View all issues with New status.

Discussion:

22.6.3.4 [variant.assign] bullet 8.4 states that an alternative-changing move assignment on std::variant is equivalent to a call to emplace. However, bullet 10.1 also states that if the construction of the new alternative exits via an exception, then the destination of the assignment is guaranteed to become valueless by exception. This is inconsistent with the specification of emplace, 22.6.3.5 [variant.mod]/11, which permits (but does not require) the variant to become valueless.

[2023-10-30; Reflector poll]

Set priority to 3 after reflector poll. "The remark is normatively redundant with the spec of emplace, strike it."

Proposed resolution:

This wording is relative to N4958.

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

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

    -6- Let j be rhs.index().

    […]

    -8- Effects:

    1. (8.1) — If neither *this nor rhs holds a value, there is no effect.

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

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

    4. (8.4) — Otherwise, equivalent to emplace<j>(get<j>(std::move(rhs))).

    […]

    -10- Remarks: […]

    1. (10.1) — If an exception is thrown during the call to Tj's move construction (with j being rhs.index()), the variant will hold no value.

    2. (10.2) — If an exception is thrown during the call to Tj's move assignment, the state of the contained value is as defined by the exception safety guarantee of Tj's move assignment; index() will be j.