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

时间:2017-07-12 02:47:20

标签: templates c++14 sfinae typetraits

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

template <typename T = std::enable_if_t<std::is_base_of<A, T> > >

但是模板仍然使用不属于继承层次结构的类型(即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;
}

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

1 个答案:

答案 0 :(得分:1)

你对enable_if的使用有点奇怪,我会做到以下几点:

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

}

或者:

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

}

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

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

编辑,解释:将enable_if_t视为满足条件时变为某种类型的内容(默认为void)。

应用此功能,您的功能如下:

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

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

您的调用仍然与假案例中的模板匹配!

现在,将此应用于我建议的代码:

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

//false case:
template <typename T>
*nonsense* templateFunction(T a) {}

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