Ответ 1
После долгих размышлений идеи, опубликованные другими ответами, и обнаружение дефектов в стандарте С++ Я думаю, что у меня есть решение, которое находится так близко, как вы может получить проверку времени компиляции для концепции Swappable
.
Это некрасиво. Он использует трюк, чтобы определить, используется ли std::swap
, предоставляя функцию с той же самой сигнатурой, что предложено T.C.. Затем мы записываем вспомогательные функции, чтобы определить, возможно ли замена, и разрешает ли оно std::swap
. Последние вспомогательные шаблоны используются, чтобы увидеть, не будет ли std::swap
noexcept. Это не использует точную семантику, как указано в стандарте С++ 14, и предполагает, что я думаю, что это будет предполагаемое поведение подкачки многомерных массивов, являющихся 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)
)
> {};