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`.