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

时间:2014-11-04 20:38:42

标签: c++

我会考虑"正确" is_swappable的实现如下:

template<class T, class U = T> struct is_swappable<T, U> : /* see below */ { }
如果T和U为std::true_type

is_swappable会从Swappable继承,否则会从std::false_type继承。


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

struct A {
    A() {}
    ~A() {}
    A(const A&) = delete;
    A(A&&) = delete;
};

显然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)) { };

有没有办法在没有编译器帮助的情况下对is_swappable进行编码?

4 个答案:

答案 0 :(得分:4)

根据@ jrok的回答,我们可以通过编写与{{1}相同签名的swap函数来判断一个不合格的std::swap来电是否会调用swap但是可以检查一个唯一的返回类型:

std::swap

然后namespace detail2 { struct tag {}; template<class T> tag swap(T&, T&); template<typename T> struct would_call_std_swap_impl { template<typename U> static auto check(int) -> std::integral_constant<bool, std::is_same<decltype( swap(std::declval<U&>(), std::declval<U&>())), tag>::value>; template<typename> static std::false_type check(...); using type = decltype(check<T>(0)); }; template<typename T> struct would_call_std_swap : would_call_std_swap_impl<T>::type { }; } 的定义变为:

is_swappable

我们还需要一个特殊的情况来交换数组:

template<typename T>
struct is_swappable :
    std::integral_constant<bool,
        detail::can_call_swap<T>::value &&
        (!detail2::would_call_std_swap<T>::value ||
        (std::is_move_assignable<T>::value &&
        std::is_move_constructible<T>::value))
    > { };

答案 1 :(得分:2)

这是我对此的看法:

#include <iostream>
#include <type_traits>

#include <utility>
namespace detail {
    using std::swap;

    template<typename T>
    struct can_call_swap_impl {

        template<typename U>
        static auto check(int)
        -> decltype( swap(std::declval<T&>(), std::declval<T&>()),
                std::true_type());

        template<typename>
        static std::false_type check(...);

        using type = decltype(check<T>(0));
    };

    template<typename T>
    struct can_call_swap : can_call_swap_impl<T>::type { };
}

template<typename T>
struct is_swappable :
    std::integral_constant<bool,
        detail::can_call_swap<T>::value &&
        std::is_move_assignable<T>::value &&
        std::is_move_constructible<T>::value
    > { };

struct A
{
    A() {}
    ~A() {}
    A(const A&) = delete;
    A(A&&) = delete;
};

int main()
{
    std::cout << is_swappable<A>{};
}

你的工作原理是它只检查是否可以调用swap,而不是它是否会在实例化时实际编译。这超出了SFINAE的范围(不是直接的背景)。

所以我只是使用requirements for std::swap扩展了测试,即T必须是MoveAssignableMoveConstructible

答案 2 :(得分:2)

经过深思熟虑,其他答案发布的想法和finding defects in the C++ standard我认为我已经得到了尽可能接近编译时检查{{1}的解决方案概念。

它不漂亮。它使用技巧通过提供具有T.C.建议的完全相同签名的函数来检测是否使用Swappable。然后我们编写辅助函数来检测是否可以进行交换,以及它是否解析为std::swap。最后的帮助器模板用于查看std::swap是否为noexcept。 This does not use the exact semantics as put forth in the C++14 standard, and assumes the what I think to be intended behaviour of swapping multidimensional arrays being noexcept.

std::swap

答案 3 :(得分:0)

怎么样:

#if __cplusplus < 201703L
template<typename...> using void_t = void;
#else
using std::void_t;
#endif

template <typename T, typename = void>
struct is_swappable : std::false_type {};
                      
template <typename T>
struct is_swappable<T, void_t<decltype(swap(std::declval<T&>(), std::declval<T&>()))>> : std::true_type {};

编辑:

namespace swap_details
{
    #if __cplusplus < 201703L
    template<typename...> using void_t = void;
    #else
    using std::void_t;
    #endif

    using std::swap;
    
    template <typename T, typename = void>
    struct is_swappable : std::false_type {};

    template <typename T>
    struct is_swappable<T, void_t<decltype(swap(std::declval<T&>(), std::declval<T&>()))>> : std::true_type {};
    
}

template <typename T>
struct is_swappable : public swap_details::is_swappable<T>
{
};