Search code examples
c++template-specializationenable-if

Class specialization with a constant value in std::enable_if


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?


Solution

  • 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.