2014-11-04 3 views
14

я хотел бы рассмотреть «правильную» для реализации is_swappable быть следующим:Что такое правильный способ реализации is_swappable для тестирования концепции Swappable?

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

is_swappable наследуется от std::true_type, если T и U является 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

начать с создания 'зЬй :: false_type swappable_test (...)' шаблон двухпараметрическое а –

+0

@PiotrS.Woops, вот почему это не работает. Отредактировано в моем вопросе и удалило ссылку coliru. – orlp

+2

И вы посмотрели [is_nothrow_swappable] (https://github.com/acmorrow/error_or/blob/master/detail/is_nothrow_swappable.hpp) на [acm] (https://stackoverflow.com/questions/ 14483105/почему-это-это-замена-и-это-nothrow замены-не-включено-в-c11)? –

ответ

1

После долгих раздумий, идеи разместили других ответы, и finding defects in the C++ standard я думаю, у меня есть решение, которое так близко, как вы можете добраться до проверки времени компиляции для Swappable концепции.

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

Не удалось скомпилировать для меня в Visual Studio 2015 с пакетом обновления 3 (SP3): ошибка C2672: «beast :: detail :: swap_adl_tests :: swap»: не найдено соответствующей перегруженной функции –

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, не будет ли она actualy компиляции, если это были экземпляры. Это вне сферы СФИНА (не непосредственный контекст).

Так что я просто продлил испытание с requirements for std::swap, то есть - T должен быть MoveAssignable и MoveConstructible.

+0

'std :: swap' выполняет переадресацию и перемещение, но я могу представить пользовательский swap, который просто меняет внутренние указатели, поэтому я бы предложил проверить 'is_move_assignable' и' is_move_constructible' только для 'std :: swap' и просто' can_call_non_std_swap' для других случаев. –

+0

Контрпример: http://coliru.stacked-crooked.com/a/84300d70c9afbebb. – orlp

+0

@PiotrS. Хороший момент, но есть ли у вас идея, как надежно сказать «std :: swap» и другие функции обмена? – jrok

4

Опираясь на ответ @ Джрок, мы можем сказать, если неквалифицированный 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)) 
    > { }; 

Нам также нужен особый случай для поменяв массивов:

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

Являются ли все подстановочные std-типы навсегда гарантированно поддерживающими move-assign и move-construction? – Deduplicator

+1

@Deduplicator 'std :: swap' требует' MoveConstructible' и 'MoveAssignable', поэтому весь существующий код, который его использует, должен использовать его с типом, который является одновременно. –

+1

Это действительно запрещает специализации с ограниченными требованиями? – Deduplicator

Смежные вопросы