Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
208 views
in Technique[技术] by (71.8m points)

c++ - What is a proper way to implement is_swappable to test for the Swappable concept?

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 inherits from std::true_type if T and U are Swappable, otherwise from std::false_type.


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

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

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 there any way to code is_swappable without compiler help?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Building on @jrok's answer, we can tell if an unqualified swap call will call std::swap by writing a swap function with the same signature as std::swap but a unique return type that can then be examined:

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

Then the definition of is_swappable becomes:

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

We also need a special case for swapping arrays:

template<typename T, std::size_t N>
struct is_swappable<T[N]> : is_swappable<T> {};

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...