SFINAE works differently in cases of type and non-type template parameters

SFINAE is about substitution. So let us substitute!

template<
  typename T, 
  std::enable_if_t<std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}

template<
  typename T, 
  std::enable_if_t<!std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}

Becomes:

template<
  class T=int, 
  int* = nullptr>
void Add(int) {}

template<
  class T=int, 
  Substitution failure* = nullptr>
void Add(int) {

template<
  class T=double, 
  Substitution failure* = nullptr>
void Add(double) {}

template<
  class T=double
  double* = nullptr>
void Add(double) {}

Remove failures we get:

template<
  class T=int, 
  int* = nullptr>
void Add(int) {}
template<
  class T=double
  double* = nullptr>
void Add(double) {}

Now remove template parameter values:

template<
  class T, 
  int*>
void Add(T) {}
template<
  class T
  double*>
void Add(T) {}

These are different templates.

Now the one that messes up:

template<
  typename T, 
  typename = typename std::enable_if<std::is_same<T, int>::value, T>::type>
void Add(T) {}

template<
  typename T, 
  typename = typename std::enable_if<!std::is_same<T, int>::value, T>::type>
void Add(T) {}

Becomes:

template<
  typename T=int, 
  typename =int>
void Add(int) {}

template<
  typename int, 
  typename = Substitution failure >
void Add(int) {}

template<
  typename T=double, 
  typename = Substitution failure >
void Add(double) {}

template<
  typename T=double, 
  typename = double>
void Add(double) {}

Remove failures:

template<
  typename T=int, 
  typename =int>
void Add(int) {}
template<
  typename T=double, 
  typename = double>
void Add(double) {}

And now template parameter values:

template<
  typename T, 
  typename>
void Add(T) {}
template<
  typename T, 
  typename>
void Add(T) {}

These are the same template signature. And that is not allowed, error generated.

Why is there such a rule? Beyond the scope of this answer. I’m simply demonstrating how the two cases are different, and asserting that the standard treats them differently.

When you use a non-type template parameter like the above, you change the template signature not just the template parameter values. When you use a type template parameter like the above, you only change the template parameter values.

Leave a Comment