Why does std::swap<std::array<int,3>> not compile?

1.7k Views Asked by At

I would like to swap two arrays of ints of some fixed size.

Counterintuitively, the following does not compile because no matching instance of swap has been found.

#include <array>
#include <utility>

int main()
{
    std::array<int,3> a, b;
    std::swap< std::array<int,3> >( a, b ); 
    return 0;
}

I find this surprising. However, replacing the swap with std::swap(a,b) compiles and (according to VSCode) has signature

inline void std::swap<int, 3UL>(std::array<int, 3UL> &__one, std::array<int, 3UL> &__two)

which I cannot make sense of either.

Q: What is going on here?

3

There are 3 best solutions below

4
463035818_is_not_an_ai On

The overload you are looking for is (from cppreference):

template< class T, std::size_t N >    
constexpr void swap( std::array<T, N>& lhs,
                     std::array<T, N>& rhs )
                         noexcept(/* see below */);

As the error reports, the compiler cant find a viable overload of std::swap() that matches std::swap<std::array<int,3>>.

This would be the "right" way to supply template arguments explictly:

#include <array>
#include <utility>

int main()
{
    std::array<int,3> a, b;
    std::swap<int,3>( a, b ); 
    return 0;
}

I doubt there is a situation where you actually want to do that though.

PS: You can also see that in the signature you get from VSCOde: std::swap<int, 3UL> is not std::swap<std::array<int,3UL>>. However, looking at implementation can be misleading sometimes and I rather suggest to consult documentation first.

7
user7860670 On

The problem seem to be caused by libstdc++ implementation. It enforces tuple-like requirement on types used in suitable "basic" variant of std::swap (source):

  template<typename _Tp>
    _GLIBCXX20_CONSTEXPR
    inline
#if __cplusplus >= 201103L
    typename enable_if<__and_<__not_<__is_tuple_like<_Tp>>,
                  is_move_constructible<_Tp>,
                  is_move_assignable<_Tp>>::value>::type
#else
    void
#endif
    swap(_Tp& __a, _Tp& __b)

even though standard does not mention such a restriction:

20.2.2
swap [utility.swap]
template<class T>
constexpr void swap(T& a, T& b) noexcept(see below );
1 Remarks: This function is a designated customization point (16.5.4.2.1) and shall not participate in overload resolution unless is_move_constructible_v<T> is true and is_move_assignable_v<T> is true. The expression inside noexcept is equivalent to: is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>
2 Requires: Type T shall be Cpp17MoveConstructible (Table 26) and Cpp17MoveAssignable (Table 28).

code works with vc++ where library only checks for is_move_constructible and is_move_assignable, as required by standard.

0
Andrew Pinski On

See https://timsong-cpp.github.io/lwg-issues/2766 wich is still an open defect against the C++ standard (library part)..