https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112942

            Bug ID: 112942
           Summary: swap(variant&, variant&) is incorrectly marked as
                    deleted
           Product: gcc
           Version: 13.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: kotonartem at protonmail dot com
  Target Milestone: ---

GCC rejects the following code (when using libstdc++):

```
#include <variant>

struct A
{};

void swap(A&, A&) = delete;

void foo()
{
    using V = std::variant<A>;
    V a, b;

    using std::swap; // this is unnecessary due to ADL
    swap(a, b);
}
```

with the following message:

```
<source>: In function 'void foo()':
<source>:14:9: error: use of deleted function
'std::enable_if_t<(!((is_move_constructible_v<_Types> && ...) &&
(is_swappable_v<_Types> && ...)))> std::swap(variant<_Types ...>&,
variant<_Types ...>&) [with _Types = {A};
enable_if_t<(!((is_move_constructible_v<_Types> && ...) &&
(is_swappable_v<_Types> && ...)))> = void]'
   14 |     swap(a, b);
      |     ~~~~^~~~~~
In file included from <source>:1:
/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/variant:1303:5: note:
declared here
 1303 |     swap(variant<_Types...>&, variant<_Types...>&) = delete;
      |     ^~~~
```

The compiler explorer link: https://godbolt.org/z/5od4s5cf3.

There is also a similar test case that expects the current behavior:
https://github.com/gcc-mirror/gcc/blob/c250ff9/libstdc%2B%2B-v3/testsuite/20_util/variant/compile.cc#L294-L300.
> ```
> // Not swappable, and variant<C> not swappable via the generic std::swap.
> struct C { };
> void swap(C&, C&) = delete;
> 
> static_assert( !std::is_swappable_v<variant<C>> );
> static_assert( !std::is_swappable_v<variant<int, C>> );
> static_assert( !std::is_swappable_v<variant<C, int>> );
> ```

I believe that this behavior is incorrect and the variant should indeed be
swappable even if `swap(A&, A&)` is deleted.

My reasoning is that in [variant.specalg] the `std::is_swappable_v<T_i>`
requirement is mentioned under "Constraints" section, which, according to
[structure.specifications], describes "the conditions for the function's
participation in overload resolution". That is in contrast to "Mandates"
section, which would render the program ill-formed.

And since the `swap(variant<A>&, variant<A>&)` overload is not considered, the
generic `template<class T> void swap(T& a, T& b)` should be chosen instead.
Unlike the former overload, it only requires `T` (aka `variant<A>`) to be both
move constructible and move assignable, which it is.

It should be noted that libc++ handles this case correctly, i.e.
`std::is_swappable_v<std::variant<A>>` is `true`.

Reply via email to