什么是正确的方法来实现is_swappable来测试可交换的概念? [英] What is a proper way to implement is_swappable to test for the Swappable concept?

查看:152
本文介绍了什么是正确的方法来实现is_swappable来测试可交换的概念?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我会考虑 is_swappable 的「正确」实作方式如下:

I would consider "proper" implementation for is_swappable to be the following:

template<class T, class U = T> struct is_swappable<T, U> : /* see below */ { }

is_swappable 继承自 std :: true_type if T和U是 可交换 ,否则从 std :: false_type

is_swappable inherits from std::true_type if T and U are Swappable, otherwise from std::false_type.

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

I have tried many things, but SFINAE just doesn't seem to work. This is a particularly nasty counterexample:

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

清楚地 A 不是 可交换 。然而任何一个通用的解决方案我可以想出没有正确处理上面的例子。

Clearly A is not Swappable. Yet any generic solution I can come up with does not properly handle the above example.

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

A SFINAE implementation I have tried, but did not work looked like this:

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 编译器帮助?

Is there any way to code is_swappable without compiler help?

推荐答案

经过多番思考,其他答案发布的想法和找出C ++标准中的缺陷我想我得到的解决方案是尽可能接近你可以得到对可交换概念的编译时检查。

After much thought, the ideas posted by the other answers, and finding defects in the C++ standard I think I've got the solution that is as close as you can get to a compile-time check for the Swappable concept.

这不漂亮。它使用一个技巧来检测 std :: swap 是否通过提供与 TC 。然后我们写帮助函数来检测交换是否可能,以及是否解析为 std :: swap 。最后一个帮助模板用于查看 std :: swap 是否为noexcept。 这不使用C ++ 14标准中提出的确切语义,并假设我认为交换多维数组的行为是 noexcept

It's not pretty. It uses a trick to detect if std::swap is used by providing a function with the exact same signature as proposed by T.C.. Then we write helper functions to detect if swapping is possible at all, and whether it resolves to std::swap. The last helper templates are used to see if std::swap will be 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.

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

这篇关于什么是正确的方法来实现is_swappable来测试可交换的概念?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆