使用std :: enable :: if和std :: is_base_of约束继承

问题描述

鉴于以下情况:

class A {};
class B : public A {};
class C : public A {};
class D : public A {};

template<typename T/*std::enable_if and std::is_base_of here*/> class X {};

当我声明X时

然后我想约束typename T强制性地成为A的子类,否则我会得到编译错误

int main()
{
   X<B> x1 = {}; //should work;
   X<C> x2 = {}; //should work;
   X<D> x2 = {}; //should work;
   X<std::string> = {};  //should generate a compiling error;
   X<int> = {};  //should generate a compiling error;
};

解决方法

或者,static_assert可能就足够了:

template<typename T>
class X {
    static_assert(std::is_base_of<A,T>::value,"!");
};
,

使用enable_if的正确语法是

template<typename T,std::enable_if_t<std::is_base_of_v<A,T>,bool> = true> 
class X {};

现在,如果AT的基数,则std::is_base_of_v<A,T>true,而std::enable_if_t变成bool,我们将其赋值true中的。如果A不是T的基数,则条件为false,并且std::enable_if_t无结果,并且该模板将作为可行的候选者被丢弃,并且会生成编译器错误。

,

第三个也是同样的常见变体

  • static_assert来自Jarod42:s answer,并且
  • 使用std::enable_if_t作为非类型模板参数的类型,默认为NathanOliver's answer中的某个值(template<...,std::enable_if_t<...,bool> = true>

是将std::enable_if_t用作类型模板参数的默认模板参数(与非类型模板参数的类型相比):

template<typename T,typename = std::enable_if_t<std::is_base_of_v<A,T>>> 
class X {};

这比使用非类型模板参数方法要简短一些。

但是它有一个缺点,那就是当用于函数模板时,它不能与SFINAE结合使用,例如仅基于谓词的相反结果来重载两个变体:

// Not OK.
template<typename T,T>>> 
void foo(const T&) { /* some impl */ }

// Error: re-definition of foo
template<typename T,typename = std::enable_if_t<!std::is_base_of_v<A,T>>>
void foo(const T&) { /* another impl */ }

由于这两个重载仅在其默认模板参数(这不是功能模板的签名的一部分)上有所不同,因此它们声明具有相同签名的两个不同的功能模板,这是非法的。

如果我们改为使用非类型模板方法,则这不是问题:

// OK
template<typename T,T>>* = nullptr> 
void foo(const T&) { /* some impl */ }


template<typename T,std::enable_if_t<!std::is_base_of_v<A,T>>* = nullptr> 
void foo(const T&) { /* another impl */ }