Enable_if with is_enum not working

MCVE:

#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); } 

I expect func(bar, 1); will match my definition of func , but g ++ reports:

 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); ^ 

Why does this not work and how to fix it?

Reference Information. This was an attempt to solve this problem .

+3
c ++ c ++ 11 sfinae
source share
3 answers
 template<typename T> bool func( typename std::enable_if< std::is_enum<T>::value, T >::type &t, int x ) 

T used above in an irreducible context. This means that he will not subtract T , since he (in the general case) requires a permutation of the arbitrary turing-complete transformation, which is impossible.

What func has is that the first argument is enum class Bar and the second is int . From this, you expect it to infer T

As long as setting T on the enum class Bar fixes the problem, C ++ does not guess. This pattern matches.

Suppose we had:

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

then

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

If someone goes from int to func , what type should be inferred for T ? This is an example of a toy: foo<T>::type can execute the Turing-complete algorithm to match T with the type in question. Inverting this, or even determining whether the converse is ambiguous, is generally not possible. Therefore, C ++ does not even try even in simple cases, since the edge between simple and complex quickly becomes fuzzy.

To fix the problem:

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

now T used in the deduced context. SFINAE still occurs, but does not block the deduction of the pattern type.

Or you can wait for C ++ 1z concepts that automate the design described above (mostly).


Considering a related question, an easy way to solve your problem is to send tags.

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

However, I would like to have three different bodies of functions:

We have 3 cases:

  • T - listing

  • T unsigned char

  • All the rest

so send:

 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>{} ); } 

normal overload rules are now used to choose between three functions. If you somehow have an enum type and an unsigned char (impossible), you will get a compile-time error.

+7
source share

You use the template argument T in a non-deduced context.

From ยง14.8.2.5 / 5 [temp.deduct.type]

Impossible contexts:
- The specified sub-name is a type specifier that was specified using an identifier with qualification.
โ€”...

To resolve the issue, move the enable_if parameter to the dummy template parameter

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

Live demo


When considering the question you were contacting, you are trying to switch between the two func definitions based on whether the first argument is enum . In this case, the above solution will not work, because the default template argument is not part of the function template signature, and you will get some definition errors.

There are two different ways to fix this; use the dummy template option

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

or use the enable_if expression in the return type

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

The errors you see are related to automatic type inference than enable_if and is_enum .

The following work.

 #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; } 

Update

As the OP has already been discovered, the use of decltype can be avoided by using the wrapper function.

 #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; } 
+2
source share

All Articles