SFINAE在类型和非类型模板参数的情况下工作方式不同

und*_*ind 20 c++ templates overloading sfinae c++11

为什么这段代码有效:

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) {}
Run Code Online (Sandbox Code Playgroud)

并且可以正确区分这两个调用:

Add(1);
Add(1.0);
Run Code Online (Sandbox Code Playgroud)

而以下代码如果编译导致重新定义Add()错误?

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) {}
Run Code Online (Sandbox Code Playgroud)

所以如果模板参数是类型,那么我们就重新定义了函数,如果它是非类型的,那么一切都好.

Yak*_*ont 19

SFINAE是关于替代的.所以让我们替换!

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) {}
Run Code Online (Sandbox Code Playgroud)

变为:

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) {}
Run Code Online (Sandbox Code Playgroud)

删除我们得到的失败:

template<
  class T=int, 
  int* = nullptr>
void Add(int) {}
template<
  class T=double
  double* = nullptr>
void Add(double) {}
Run Code Online (Sandbox Code Playgroud)

现在删除模板参数值:

template<
  class T, 
  int*>
void Add(T) {}
template<
  class T
  double*>
void Add(T) {}
Run Code Online (Sandbox Code Playgroud)

这些是不同的模板.

现在是一个混乱的人:

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) {}
Run Code Online (Sandbox Code Playgroud)

变为:

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) {}
Run Code Online (Sandbox Code Playgroud)

删除失败:

template<
  typename T=int, 
  typename =int>
void Add(int) {}
template<
  typename T=double, 
  typename = double>
void Add(double) {}
Run Code Online (Sandbox Code Playgroud)

现在模板参数值:

template<
  typename T, 
  typename>
void Add(T) {}
template<
  typename T, 
  typename>
void Add(T) {}
Run Code Online (Sandbox Code Playgroud)

这些是相同的模板签名.这是不允许的,错误产生.

为什么会有这样的规则?超出了这个答案的范围.我只是简单地说明这两种情况是如何不同的,并断言标准对待它们的方式不同.

当您使用如上所述的非类型模板参数时,您更改模板签名而不仅仅是模板参数值.使用类似上面的类型模板参数时,只更改模板参数值.


Dan*_*ani 1

我认为问题在于,即使默认模板参数无法通过为其指定不同的值来编译,您也可以使用该函数。想一想如果您在 add 调用中指定了两个模板参数会发生什么。