The compiler clearly told you what the problem is: in your template declaration you specified an extra template non-type parameter that cannot be deduced. How do you expect the compiler to deduce a proper value for that non-type parameter? From what?
This is exactly why the above technique for using std::enable_if
requires a default argument. This is a dummy parameter, so the default argument value does not matter (0
is a natural choice).
You can reduce your example to a mere
template <typename T, T x>
void foo(T t) {}
int main()
{
foo(42);
}
Producing
error: no matching function for call to 'foo(int)'
note: template argument deduction/substitution failed:
note: couldn't deduce template parameter 'x'
The compiler can deduce what T
is (T == int
), but there's no way for the compiler to deduce the argument for x
.
Your code is exactly the same, except that your second template parameter is left unnamed (no need to give a name to a dummy parameter).
Judging by your comments, you seem to be confused by the presence of keyword typename
in the declaration of the second parameter in your code, which makes you believe that the second parameter is also a type parameter. The latter is not true.
Note, that in the second parameter's declaration keyword typename
is used in a completely different role. This keyword simply disambiguates the semantics of
std::enable_if<std::is_class<T>::value, T>::type
It tells the compiler that nested name type
actually represents a name of a type, not of something else. (You can read about this usage of typename
here: Why do we need typename here? and Where and why do I have to put the "template" and "typename" keywords?)
This usage of typename
does not turn the second parameter of your template into a type parameter. The second parameter of your template is still a non-type parameter.
Here's another simplified example that illustrates what happens in your code
struct S { typedef int nested_type; };
template <typename T, typename T::nested_type x>
void bar(T t)
{}
int main()
{
S s;
bar<S, 42>(s);
}
Note that even though the declaration of the second parameter begins with a typename
, it still declares a non-type parameter.
enable_if
is the default argument. It looks like this:typename = std::enable_if<...>::type
. It's the template type parameter (with its name omitted) given a default value. In your code, you have a non-type template parameter of typestd::enable_if<...>::type
, with no way to deduce it. – DougieT
, note the= 0
default template argument won't compile if classT
can't be copy-initialized from a0
. This is why you'll see some code use insteadtemplate <
..., typename std::enable_if<
expr>::type* = nullptr>
. In that usage, the type resulting fromenable_if
isvoid
if expr is true, so the last template parameter type is alwaysvoid*
no matter what the other template parameter(s) are. – Behistun