2015-12-10 4 views
4

Я хотел бы использовать SFINAE для перегрузки шаблона функции на основе того, объявляет ли аргумент шаблона тип T. Это где я был в состоянии получить до сих пор:Проверка наличия объявления типа для SFINAE

struct C1 { 
    using T = int; 
}; 

struct C2 { 
    using T = void; // but I would really like 
        // to not require T at all 
}; 

// For classes that declare T 
template <class C> 
void f(C &c, typename std::enable_if<!std::is_same<typename C::T, void>::value, 
            int>::type = 0) { 
    (void)c; 
    std::cout << "With T" << std::endl; 
} 

// For classes that do not declare T (for now, T must be declared void) 
template <class C> 
void f(C &c, typename std::enable_if<std::is_same<typename C::T, void>::value, 
            int>::type = 0) { 
    (void)c; 
    std::cout << "Without T" << std::endl; 
} 

int main() { 
    C2 c; 
    f(c); 
    return 0; 
} 

Как (если вообще) можно ли изменить это решение таким образом, что C2 не нужно было объявлять T вообще? То есть Я хотел бы иметь две перегрузки: один для классов, объявляющих T, и один для классов, которые этого не делают.

ответ

4
#include <type_traits> 

template <typename...> 
struct voider { using type = void; }; 

template <typename... Ts> 
using void_t = typename voider<Ts...>::type; 

template <typename C, typename = void_t<>> 
struct has_t : std::false_type {}; 

template <typename C> 
struct has_t<C, void_t<typename C::T>> : std::true_type {}; 

template <typename C> 
auto f(C& c) 
    -> typename std::enable_if<has_t<C>::value>::type 
{ 
} 

template <typename C> 
auto f(C& c) 
    -> typename std::enable_if<!has_t<C>::value>::type 
{ 
} 

DEMO

+0

Почему это не работает с этим определением 'void_t':' template struct void_t {}; ' – AlwaysLearning

+0

@AlwaysLearning, какой тип' void_t {} 'имел? Вы имели в виду 'void_t = void;'? –

+0

Прости, да. template using void_t = void; – AlwaysLearning

4

Вы можете сделать тип признака в соответствии с вашими потребностями:

template<typename Type, typename Enable=void> 
struct has_t : std::false_type {}; 

template<typename Type> 
struct has_t<Type, void_t<Type::T>> : std::true_type {}; 

void_t реализуется следующим образом:

template<typename...> 
struct voider { 
    using type = void; 
}; 

template<class... Ts> using void_t = typename voider<Ts...>::type; 

Это void_t реализация гарантированно работы в C++ 11.

Теперь вы можете использовать этот тип признака, как это:

template<typename C, typename std::enable_if<has_t<typename std::decay<C>::type>::value, int>::type = 0> 
void f(C&& c) { 
    // ... C::T exists 
} 

template<typename C, typename std::enable_if<!has_t<typename std::decay<C>::type>::value, int>::type = 0> 
void f(C&& c) { 
    // ... C::T doesn't exists 
} 

Вы должны распадаться типа, как это будет ссылка. Так как typedef внутри C не находится внутри, допустим, C&, вы должны сначала удалить ссылку.

+0

Что означает синтаксис 'template ' означает? – AlwaysLearning

+0

Это означает, что это параметр вариационного шаблона. Он принимает любое количество типов, и этот случай всегда будет равен void. –

+0

Я снова пришел к этому коду и попробовал, но он не компилируется ... – AlwaysLearning