I was writing a partial specialization for a class when the template parameter is derived from a specific type. My implementation is as follow -
struct base {};
struct derived: public base {};
template <typename T, typename=void>
struct foo {
foo() { std::cout << "Version 1" << std::endl; }
};
template <typename T>
struct foo <T, typename std::enable_if<std::is_base_of<base, T>::value>::type> {
foo() { std::cout << "Version 2" << std::endl; }
};
This works as expected - when I instantiate foo<int>
, the constructor prints Version 1
and when I instantiate foo<derived>
, the constructor prints Version 2
.
Now, I was testing something and wanted to disable the partial specialization, so I just changed it to -
template <typename T>
struct foo <T, typename std::enable_if<false>::type> {
foo() { std::cout << "Version 2" << std::endl; }
};
I just replaced std::is_base_of<base, T>::value
with the constant false
. But this time the compiler started issuing warnings like std::enable_if<false, void>
doesn't have type
.
Why does it behave this way? Why does SFINAE not kick in here? Why does the parameter to std::enable_if
HAVE to depend on the template parameter?
Why does SFINAE not kick in here?
SFINAE stands for "substitution failure is not an error". If your argument to std::enable_if
is not dependent, then there is no substitution and so it makes sense that it wouldn't apply.
Specifically the standard says the program is ill-formed, no diagnostic required, if
a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, [...]
(from [temp.res.general]/8.4) of the post-C++20 draft N4868)
std::enable_if<false>::type
is not dependent and ill-formed immediately following the definition of your template.