如何使用 SFINAE 的 std::enable_if_t 和 std::is_base_of 检查编译时继承?

Con*_*tin 4 templates sfinae type-traits c++14

我试图在编译时将函数的模板类型限制为特定类及其子类。为了实现这一点,我使用类型特征std::enable_if_tstd::is_base_of如下所示:

template <typename T = std::enable_if_t<std::is_base_of<A, T> > >
Run Code Online (Sandbox Code Playgroud)

但模板仍然使用不属于继承层次结构(即int)的类型进行编译。下面是该问题的 MCVE:

class A {
public:
  A(float a) : a(a) {}
  float a;
};
class B : public A{
public:
  B(float a) : A(a) {}
};

template <typename T = std::enable_if_t<std::is_base_of<A, T> > >
void templateFunction(T a) {

}

int main() {
  templateFunction<A>(A(1.0f)); // OK -> std::is_base_of<A, A>
  templateFunction<B>(B(1.0f)); // OK -> std::is_base_of<A, B>

  templateFunction<int>(1); // Should not compile! int is not a subclass of A -> std::is_base_of<A, int>
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这在 Visual Studio 2017 下编译没有任何错误,但根据我的理解,模板函数的最后实例化不应编译。我对类型特征的使用是否有问题,或者 Visual Studios SFINAE 实现是否有问题?

小智 6

您对 enable_if 的使用有点奇怪,我会执行以下操作:

template <typename T>
std::enable_if_t<std::is_base_of<A, T>::value> templateFunction(T a) {

}
Run Code Online (Sandbox Code Playgroud)

更好的解决方案:完全忘记 SFINAE:

template <typename T>
void templateFunction(T a) {
  static_assert(std::is_baseOf<A,T>(), "only subclasses, please");
}
Run Code Online (Sandbox Code Playgroud)

编辑,解释:将enable_if_t视为如果满足条件则成为类型的东西(默认为void)。

应用这个,你的函数如下:

// true case:
template <typename T = void>
void templateFunction(T a) {}

// false case:
template <typename T = nonsense>
void templateFunction(T a) {}
Run Code Online (Sandbox Code Playgroud)

在错误情况下,您的调用仍然与模板匹配!

现在,将其应用到我建议的代码中:

//true case:
template <typename T>
void templateFunction(T a) {}

//false case:
template <typename T>
*nonsense* templateFunction(T a) {}
Run Code Online (Sandbox Code Playgroud)

在这里,在 false 情况下该函数根本无法存在,因此会产生编译错误。