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);
to match my definition of func
however 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 isn't this working and how do I fix it?
Background: This was an attempted solution to this problem
You're using the template argument
T
in a non-deduced context.From §14.8.2.5/5 [temp.deduct.type]
To fix the problem move the
enable_if
to a dummy template parameterLive demo
Looking at the question you linked to, you're trying to switch between two definitions of
func
based on whether the first argument is anenum
. In that case the above solution will not work because a default template argument is not part of the function template's signature, and you'll end up with multiple definition errors.There are two different ways to fix that, use a dummy template parameter
or use the
enable_if
expression in the return typeThe errors you are seeing have to do with automatic type deduction than
enable_if
andis_enum
.The following works.
Update
As already discovered by the OP, use of
decltype
can be avoided by using a wrapper function.T
is used above in a non-deduced context. This means it won't deduct theT
, as it (in the general case) requires reversing an arbitrary turing-complete transformation, which is impossible.What
func
has is that the first argument is aenum class Bar
, and the second is anint
. From this you expect it to deduceT
.While setting
T
toenum class Bar
does solve the problem, C++ doesn't guess. It pattern matches.Suppose we had:
then
If someone passes a
int
tofunc
, what type should be deduced forT
? This is a toy example:foo<T>::type
can execute a Turing-complete algorithm to mapT
to the type in question. Inverting that or even determining if the inverse is ambiguous, is not possible in the general case. So C++ doesn't even try, even in the simple cases, as the edge between simple and complex gets fuzzy quickly.To fix your problem:
now
T
is used in a deduced context. The SFINAE still occurs, but doesn't block template type deduction.Or you can wait for C++1z concepts, which automate the above construct (basically).
Looking at the linked question, the easy way to solve your problem is with tag dispatching.
However I would like to have three different function bodies:
We have 3 cases:
T being an enum
T being unsigned char
Everything else
so, dispatch:
now normal overload rules are used to pick between the 3 functions. If you somehow have a type that is both
enum
andunsigned char
(impossible), you get a compile-time error.