2015-04-21 3 views
3

MCVE:enable_if с is_enum не работает

#include <type_traits> 

template<typename T> 
bool func(typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x) 
{ 
} 

enum class Bar { a,b,c }; 

int main() 
{ 
    Bar bar{Bar::a}; 
    func(bar, 1); 
} 

Я ожидаю func(bar, 1); соответствовать мое определение func отчетов однако г ++:

sfi.cc: In function 'int main()': 
sfi.cc:13:17: error: no matching function for call to 'func(Bar&, int)' 
     func(bar, 1); 
       ^
sfi.cc:13:17: note: candidate is: 
sfi.cc:4:10: note: template<class T> bool func(typename std::enable_if<std::is_e 
num<_Tp>::value, T>::type&, int) 
    bool func(typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x) 
     ^
sfi.cc:4:10: note: template argument deduction/substitution failed: 
sfi.cc:13:17: note: couldn't deduce template parameter 'T' 
     func(bar, 1); 
       ^

Почему это не работает и как это исправить ?

фона: Это была попытка решения this problem

ответ

2

Ошибки, которые вы видите должны сделать с автоматическим типом дедукции, чем enable_if и is_enum.

Следующие работы.

#include <iostream> 
#include <type_traits> 

template<typename T> 
bool func(typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x) 
{ 
    return true; 
} 

enum class Bar { a,b,c }; 

int main() 
{ 
    Bar bar{Bar::a}; 
    std::cout << func<decltype(bar)>(bar, 1) << std::endl; 
} 

Обновление

Как уже было обнаружено ОП, использование decltype можно избежать, используя функцию-оболочку.

#include <iostream> 
#include <type_traits> 

template <typename T> 
bool func(typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x) 
{ 
    return true; 
} 

template <typename T> 
bool func2(T &t, int x) 
{ 
    return func<T>(t,x); 
} 

enum class Bar { a,b,c }; 

int main() 
{ 
    Bar bar{Bar::a}; 
    std::cout << func2(bar, 1) << std::endl; 
} 
+0

ОК, это что-то.Кажется, что работает и с оберткой: 'template bool func2 (T & t, int x) {return func (t, x); } ' –

7
template<typename T> 
bool func(typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x) 

T используется выше в не выводится контексте. Это означает, что он не будет вычитать T, так как он (в общем случае) требует обращения вспять произвольное преобразование с полным преобразованием, что невозможно.

func имеет то, что первым аргументом является enum class Bar, а второй - int. Из этого вы ожидаете, что он выведет T.

При установке T на enum class Bar действительно ли проблема, C++ не угадывает. Этот шаблон совпадает.

Предположим, что у нас было:

template<class T> 
struct blah { using type=int; }; 
template<> 
struct blah<int> { using type=double; }; 

затем

template<class T> 
bool func(typename blah<T>::type); 

Если кто-то пропускает int к func, какой тип должен быть выведен на T? Это пример игрушки: foo<T>::type может выполнить алгоритм Turing-complete для отображения T типа, о котором идет речь. Инвертирование этого или даже определение того, является ли обратное неоднозначным, в общем случае невозможно. Поэтому C++ даже не пытается даже в простых случаях, так как край между простым и сложным быстро становится нечетким.

Чтобы устранить проблему:

template<class T,class=typename std::enable_if< std::is_enum<T>::value >::type> 
bool func(T &t, int x) { 
} 

Теперь T используется в выведенного контексте. SFINAE по-прежнему происходит, но не блокирует вычет типа шаблона.

Или вы можете дождаться концепций C++ 1z, которые автоматизируют описанную выше конструкцию (в основном).


Рассматривая связанный вопрос, простой способ решить вашу проблему - отправка меток.

template<typename T> 
bool func(T &t, int x) 
{ 
    // do stuff... 
} 

Однако я хотел бы иметь три различных функциональных органов:

У нас есть 3 случая:

  • T является перечисление

  • T быть символ без знака

  • Все остальное

так, отправка:

namespace details { 
    template<class T> 
    bool func(T& t, int x, std::true_type /* is_enum */, std::false_type) { 
    } 
    template<class T> 
    bool func(T& t, int x, std::false_type, std::true_type /* unsigned char */) { 
    } 
    template<class T> 
    bool func(T& t, int x, std::false_type, std::false_type) { 
    // neither 
    } 
} 
template<class T> 
bool func(T& t, int x) { 
    return details::func(t, x, std::is_enum<T>{}, std::is_same<unsigned char, T>{}); 
} 

Теперь нормальные правила перегрузки используются, чтобы выбрать между 3 функциями. Если у вас есть тип enum и unsigned char (невозможно), вы получите ошибку времени компиляции.

3

Вы используете аргумент шаблона T в невыводимом контексте.

От §14.8.2.5/5 [temp.deduct.type]

В не-выведены контекстах:
вложенного имени спецификатор типа, который был указан с использованием идентификатора .
— ...

Чтобы устранить проблему перемещения enable_if в фиктивный параметр шаблона

template<typename T, 
     typename = typename std::enable_if< std::is_enum<T>::value, T >::type> 
bool func(T &t, int x) 
{ 
    // ... 
} 

Live demo


Глядя на вопрос, который вы связаны, вы пытаясь переключиться между двумя определениями func на основе того, является ли первый аргумент enum. В этом случае вышеупомянутое решение не будет работать, потому что аргумент шаблона по умолчанию не является частью подписи шаблона функции, и вы получите несколько ошибок определения.

Есть два способа исправить это, используйте фиктивный параметр шаблона

template<typename T, 
     typename std::enable_if< std::is_enum<T>::value, int >::type* = nullptr> 
bool func(T &t, int x) 
{ 
    // ... 
} 

или использовать выражение enable_if в типе возвращаемого

template<typename T> 
typename std::enable_if< std::is_enum<T>::value, bool >::type 
    func(T &t, int x) 
{ 
    // ... 
} 
+0

Правильно, я получал несколько ошибок определения при попытке вашего первого предложения и не мог понять, почему они появлялись, поэтому спасибо, что объяснили это также :) –

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