template<bool F, typename = std::enable_if_t<F>>
auto func1() -> int { return 0; }
template<bool F, typename = std::enable_if_t<!F>>
auto func1() -> int { return 0; }
template<bool F, std::enable_if_t<F, int> = 0>
auto func2() -> int { return 0; }
template<bool F, std::enable_if_t<!F, int> = 0>
auto func2() -> int { return 0; }
Here are two sets of overloaded functions: func1()
and func2()
.
func1()
uses type parameter for SFINAE and func2()
uses non-type parameter for SFINAE.
When compiling these functions, func1()
causes compilation error but func2()
does not. Why does SFINAE work for func2()
and fails for func1()
?
Because in the first case, a SFINAE failure remove only the default for a template parameter.
// in case F is true, you have
template <bool F, typename = void>
int func1() { return 0; }
template <bool F, typename> // no more default but still available
int func1() { return 0; }
So doesn't disable the function, and you have two definitions of the same function (a default value doesn't change a function signature) so, as pointed by Jarod42 (thanks), you have a violation of the One Definition Rule.
In the second case you remove a template parameter, so you destroy the function, so no collision anymore.
template <bool F, int = 0>
int func2() { return 0; }
// template<bool F, ...> // function removed
// int func2() { return 0; }
You can verify that, in the first case, also the "disabled" function is still available, with a test with a single function
template <bool F, typename = std::enable_if_t<F>>
int foo ()
{ return 0; }
and calling it with true
, false
and false, void
.
foo<true>(); // compile: foo<true, void>() called
foo<false>(); // compilation error: no second template paramenter
foo<false, void>(); // compile: explicit second template parameter