This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofC++17 status.
variant move-assignment more exception safeSection: 22.6.3.4[variant.assign]Status:C++17Submitter: United StatesOpened: 2017-02-03Last modified: 2020-09-06
Priority:Not Prioritized
View otheractive issues in [variant.assign].
View all otherissues in [variant.assign].
View all issues withC++17 status.
Discussion:
Addresses US 119 and CH 7The 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 thevalueless_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 withmuch input from Tomasz.
[Kona 2017-03-02]
Accepted as Immediate to resolve NB comment.
Proposed resolution:
This wording is relative toN4640.
Modify 22.6.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
-1-Effects:jberhs.index().
(1.1) — If neither
*thisnorrhsholds a value, there is no effect. Otherwise,(1.2) — if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value. Otherwise,(1.3) — if
index() ==j, assigns the value contained inrhs.index()rhsto the value contained in*this. Otherwise,(1.?) — if
is_nothrow_copy_constructible_v<Tj> || !is_nothrow_move_constructible_v<Tj>istrue, equivalent toemplace<j>(get<j>(rhs)). Otherwise,(1.4) —equivalent to
operator=(variant(rhs))copies the value contained in.rhsto a temporary, then destroys any value contained in*this. Sets*thisto hold the same alternative index asrhsand initializes the value contained in*thisas if direct-non-list-initializing an object of typeTjwithstd::forward<Tj>(TMP), withTMPbeing the temporary andjbeingrhs.index()-2-Returns:
-3-Postconditions:*this.index() == rhs.index().-4-Remarks: This function shall not participate in overload resolution unlessis_copy_constructible_v<Ti>is&& is_move_constructible_v<Ti>&& is_copy_assignable_v<Ti>truefor alli.
(4.1) — If an exception is thrown during the call […]
(4.2) — If an exception is thrown during the call […]
(4.3) — If an exception is thrown during the call […]variant& operator=(variant&& rhs) noexcept(see below);Let
-5-Effects:jberhs.index().
(5.1) — If neither
*thisnorrhsholds a value, there is no effect. Otherwise,(5.2) — if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value. Otherwise,(5.3) — if
index() ==j, assignsrhs.index()get<j>(std::move(rhs))to the value contained in*this, with. Otherwise,jbeingindex()(5.4) —equivalent to
emplace<j>(get<j>(std::move(rhs)))destroys any value contained in.*this. Sets*thisto hold the same alternative index asrhsand initializes the value contained in*thisas if direct-non-list-initializing an object of typeTjwithget<j>(std::move(rhs))withjbeingrhs.index()[…]
[…]
template <class T> variant& operator=(T&& t) noexcept(see below);-8- […]
-9-Effects:
(9.1) — If *this holds a
Tj, assignsstd::forward<T>(t)to the value contained in*this. Otherwise,(9.?) — if
is_nothrow_constructible_v<Tj, T> || !is_nothrow_move_constructible_v<Tj>istrue, equivalent toemplace<j>(std::forward<T>(t)). Otherwise,(9.3) —equivalent to
operator=(variant(std::forward<T>(t)))destroys any value contained in.*this, sets*thisto hold the alternative typeTjas selected by the imaginary function overload resolution described above, and direct-initializes the contained value as if direct-non-list-initializing it withstd::forward<T>(t)