实现is_swappable以测试Swappable概念的正确方法是什么?

orl*_*rlp 15 c++

我认为"适当"的实现is_swappable如下:

template<class T, class U = T> struct is_swappable<T, U> : /* see below */ { }
Run Code Online (Sandbox Code Playgroud)

is_swappablestd::true_typeT和U 继承Swappable,否则从std::false_type.


我尝试了很多东西,但SFINAE似乎没有用.这是一个特别讨厌的反例:

struct A {
    A() {}
    ~A() {}
    A(const A&) = delete;
    A(A&&) = delete;
};
Run Code Online (Sandbox Code Playgroud)

显然A不是Swappable.然而,我能提出的任何通用解决方案都无法正确处理上述示例.

我试过的SFINAE实现,但没有工作看起来像这样:

namespace with_std_swap {
    using std::swap;

    template<class T, class U, class =
        decltype(swap(std::declval<T&>(), std::declval<U&>()))>
    std::true_type swappable_test(int);

    template<class, class> std::false_type swappable_test(...);
}

template<class T, class U = T>
struct is_swappable
: decltype(with_std_swap::using_std_swap::swappable_test<T, U>(0)) { };
Run Code Online (Sandbox Code Playgroud)

is_swappable没有编译器帮助的代码?

orl*_*rlp 2

经过深思熟虑,其他答案提出的想法,并发现 C++ 标准中的缺陷,我认为我已经得到了尽可能接近该Swappable概念的编译时检查的解决方案。

这并不漂亮。它使用一种技巧来检测是否使用了TCstd::swap所提议的具有完全相同签名的函数。然后我们编写辅助函数来检测交换是否可能,以及它是否解析为. 最后一个辅助模板用于查看是否为 noexcept。这没有使用 C++14 标准中提出的确切语义,并假设我认为交换多维数组的预期行为是.std::swapstd::swapnoexcept

namespace detail {
    namespace swap_adl_tests {
        // if swap ADL finds this then it would call std::swap otherwise (same signature)
        struct tag {};

        template<class T> tag swap(T&, T&);
        template<class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);

        // helper functions to test if an unqualified swap is possible, and if it becomes std::swap
        template<class, class> std::false_type can_swap(...) noexcept(false);
        template<class T, class U, class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
        std::true_type can_swap(int) noexcept(
            noexcept(swap(std::declval<T&>(), std::declval<U&>()))
        );

        template<class, class> std::false_type uses_std(...);
        template<class T, class U>
        std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag> uses_std(int);

        template<class T>
        struct is_std_swap_noexcept : std::integral_constant<bool,
            std::is_nothrow_move_constructible<T>::value &&
            std::is_nothrow_move_assignable<T>::value
        > { };

        template<class T, std::size_t N>
        struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> { };

        template<class T, class U>
        struct is_adl_swap_noexcept : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> { };
    }
}

template<class T, class U = T>
struct is_swappable : std::integral_constant<bool, 
    decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
        (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
            (std::is_move_assignable<T>::value && std::is_move_constructible<T>::value))
> {};

template<class T, std::size_t N>
struct is_swappable<T[N], T[N]> : std::integral_constant<bool, 
    decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
        (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value ||
            is_swappable<T, T>::value)
> {};

template<class T, class U = T>
struct is_nothrow_swappable : std::integral_constant<bool, 
    is_swappable<T, U>::value && (
        (decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
            detail::swap_adl_tests::is_std_swap_noexcept<T>::value)
        ||
        (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
            detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value)
    )
> {};
Run Code Online (Sandbox Code Playgroud)