我会考虑"正确" 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
进行编码?
答案 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
必须是MoveAssignable
和MoveConstructible
。
答案 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>
{
};