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?
SFINAE stands for "substitution failure is not an error". If your argument to
std::enable_ifis 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
(from [temp.res.general]/8.4) of the post-C++20 draft N4868)
std::enable_if<false>::typeis not dependent and ill-formed immediately following the definition of your template.