From the cppreference.com article on std::enable_if
,
Notes
A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.
/*** WRONG ***/
struct T {
enum { int_t,float_t } m_type;
template <
typename Integer,
typename = std::enable_if_t<std::is_integral<Integer>::value>
>
T(Integer) : m_type(int_t) {}
template <
typename Floating,
typename = std::enable_if_t<std::is_floating_point<Floating>::value>
>
T(Floating) : m_type(float_t) {} // error: cannot overload
};
/* RIGHT */
struct T {
enum { int_t,float_t } m_type;
template <
typename Integer,
typename std::enable_if_t<std::is_integral<Integer>::value, int> = 0
>
T(Integer) : m_type(int_t) {}
template <
typename Floating,
typename std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0
>
T(Floating) : m_type(float_t) {} // OK
};
I'm having a hard time wrapping my head around why the *** WRONG ***
version doesn't compile while the *** RIGHT***
version does. The explanation and the example are cargo cult to me. All that has been done in the above is to change a type template parameter to a non-type template parameter. To me, both versions should be valid because both rely on std::enable_if<boolean_expression,T>
having a typedef member named type
, and std::enable_if<false,T>
does not have such a member. A substitution failure (which is not an error) should result in both versions.
Looking at the standard, it says that in [temp.deduct] that
when a function template specialization is referenced, all of the template arguments shall have values
and later that
if a template argument has not been deduced and its corresponding template parameter has a default argument, the template argument is determined by substituting the template arguments determined for preceding template parameters into the default argument. If the substitution results in an invalid type, as described above, type deduction fails.
That this type deduction failure is not necessarily an error is what SFINAE is all about.
Why does changing the typename template parameter in the *** WRONG ***
version to a non-typename parameter make the *** RIGHT ***
version "right"?