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.

### 2749. `swappable` traits for `variant`s

Section: 20.7.3.7 [variant.swap], 20.7.10 [variant.specalg] Status: C++17 Submitter: Agustín K-ballo Bergé Opened: 2016-07-19 Last modified: 2017-07-30

Priority: 1

View all issues with C++17 status.

Discussion:

`variant` does not play nice with swappable traits, the non-member specialized `swap` overload is not SFINAE friendly. On the other hand, the member `swap` is SFINAE friendly, albeit with an incomplete condition, when arguably it shouldn't be. Given the Effects, Throws, and Remarks clauses, the SFINAE condition should include `is_move_constructible_v` and `is_move_assignable_v` to account for the involvement of `variant`'s move constructor and move assignment operator (the `noexcept` specification is correct as is, since the move assignment operator would only be called for `variant`s with different alternatives). This SFINAE condition should apply to the non-member `swap` overload, while the member `swap` should require all alternatives are swappable (as defined by 16.4.4.3 [swappable.requirements]).

[2016-07 Chicago]

Monday: P1 - review later in the week

Fri PM: Move to Tentatively Ready

Previous resolution [SUPERSEDED]:

> This wording is relative to N4606.

> 1. Modify 20.7.3.7 [variant.swap] as indicated:

>    >     void swap(variant& rhs) noexcept(see below);

>    > > -?- Requires: Lvalues of type `Ti` shall be swappable and `is_move_constructible_v<Ti> && is_move_assignable_v<Ti>` is `true` for all `i`.

>    > > […]

>    > > -3- Remarks: This function shall not participate in overload resolution unless `is_swappable_v<Ti>` is `true` for all `i`. If an exception is thrown during the call to function `swap(get<i>(*this), get<i>(rhs))`, the states of the contained values of `*this` and of `rhs` are determined by the exception safety guarantee of swap for lvalues of `Ti` with `i` being `index()`. If an exception is thrown during the exchange of the values of `*this` and `rhs`, the states of the values of `*this` and of `rhs` are determined by the exception safety guarantee of `variant`'s move constructor and move assignment operator. The expression inside `noexcept` is equivalent to the logical AND of `is_nothrow_move_constructible_v<Ti> && is_nothrow_swappable_v<Ti>` for all `i`.

> 2. Modify 20.7.10 [variant.specalg] as indicated:

>    >     template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);

>    > > -1- Effects: Equivalent to `v.swap(w)`.

>    > > -2- Remarks: This function shall not participate in overload resolution unless `is_move_constructible_v<Ti> && is_move_assignable_v<Ti> && is_swappable_v<Ti>` is `true` for all `i`. The expression inside `noexcept` is equivalent to `noexcept(v.swap(w))`.

[2016-08-13, Reopened by Casey Carter]

It is possible to exchange the value of two variants using only move construction on the alternative types, as if by

    auto tmp = move(x);
    x.emplace<i>(move(y));
    y.emplace<j>(move(tmp));

where i is `y.index()` and j is `tmp.index()`. Consequently, `variant`'s member swap need not require move assignable alternatives.

[2016-09-09 Issues Resolution Telecon]

Move to Tentatively Ready

Proposed resolution:

This wording is relative to N4606.

1. Modify 20.7.3.7 [variant.swap] as indicated:

   >     void swap(variant& rhs) noexcept(see below);

   > > -?- Requires: Lvalues of type `Ti` shall be swappable and `is_move_constructible_v<Ti>` shall be `true` for all `i`.

   > > […]

   > > -2- Throws: If `index() == rhs.index()`, a Any exception thrown by `swap(get<i>(*this), get<i>(rhs))` with i being `index()` and `variant`'s move constructor and assignment operator. Otherwise, any exception thrown by the move constructor of `Ti` or `Tj` with `i` being `index()` and `j` being `rhs.index()`.

   > > -3- Remarks: This function shall not participate in overload resolution unless `is_swappable_v<Ti>` is `true` for all `i`. If an exception is thrown during the call to function `swap(get<i>(*this), get<i>(rhs))`, the states of the contained values of `*this` and of `rhs` are determined by the exception safety guarantee of swap for lvalues of `Ti` with `i` being `index()`. If an exception is thrown during the exchange of the values of `*this` and `rhs`, the states of the values of `*this` and of `rhs` are determined by the exception safety guarantee of `variant`'s move constructor and move assignment operator. The expression inside `noexcept` is equivalent to the logical AND of `is_nothrow_move_constructible_v<Ti> && is_nothrow_swappable_v<Ti>` for all `i`.

2. Modify 20.7.10 [variant.specalg] as indicated:

   >     template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);

   > > -1- Effects: Equivalent to `v.swap(w)`.

   > > -2- Remarks: This function shall not participate in overload resolution unless `is_move_constructible_v<Ti> && is_swappable_v<Ti>` is `true` for all `i`. The expression inside `noexcept` is equivalent to `noexcept(v.swap(w))`.