确保typename类型是Derived of Base

时间:2016-02-26 22:35:26

标签: c++ templates

我有这种代码

template <typename D, typename T>
class tl1 {
    std::list<T> mTs ;
public:
    T & getTbyName() const ;
}

template <typename T, typename C>
class tl2 {
public:
    std::string getName() { return mName ; }
private:
    C & mC ;
    std::string mName
}

class c2 ;

class cl1 : tl1<cl1, cl2>  {

}
class cl2 : tl2<cl2, cl1>  {

}

我如何检查(compile timeT cl2类型或derived from cl2Ccl1类型还是{{ 1}}。我需要确定cl2类型或derived from cl1将是一团糟。

感谢您的时间 昆汀

3 个答案:

答案 0 :(得分:6)

您可以使用static_assertstd::is_base_of

#include <type_traits>

struct B {};

template <typename T>
class C {
    static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
};

struct D : public B {};
struct KO {};


template class C<B>;
template class C<D>;
template class C<KO>;

int main()
{
}
main.cpp: In instantiation of 'class C<KO>':
main.cpp:16:16:   required from here
main.cpp:7:5: error: static assertion failed: T should inherit from B
     static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
     ^
======= clang =======
main.cpp:7:5: error: static_assert failed "T should inherit from B"
    static_assert(std::is_base_of<B, T>::value, "T should inherit from B");
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:16: note: in instantiation of template class 'C<KO>' requested here
template class C<KO>;
               ^

Online Demo

答案 1 :(得分:1)

使用std::is_base_of中的<type_traits>std::enable_if

template <typename T,
  typename = typename std::enable_if<std::is_base_of<cl2, T>::value>::type>
class cl1 {
    std::list<T> mTs ;
}

答案 2 :(得分:1)

当你想弄清楚A是否是B的基础时,你的编译器需要知道类型定义,或者A和B必须是相同的(A是A的基础)。

因此,您要检查一个模板参数是否来自前向声明的类型。

从您的later post判断,您还需要检查您的CRTP基类是否实际用作其中一个参数的基类。

在后一种情况下,编译器在CRTP基类专用时没有所有信息,因为尚未定义派生类。

因此,您必须将评估推迟到编译器具有必要信息的后期阶段。为此,让我们创建一个帮助器:

#include <utility>

class cl2;
class cl1;

template <typename Defer, typename Base, typename Derived>
struct deferred_is_base_of : std::is_base_of<Base, Derived>
{
};

理论上,这个类可以在以后专门化,因此编译器不能假设defered_is_base_ofstd::is_base_of相同。

假设我们需要一个构造函数(任何其他“必需”函数也可以):

template <typename Derived, typename Value>
class tl1
{
public:
  template <typename Defer = void>
  tl1()
  {
    static_assert(deferred_is_base_of<Defer, cl2, Value>::value,
                  "Value has to be derived from cl2");

    static_assert(deferred_is_base_of<Defer, tl1, Derived>::value,
                  "Derived has to be derived from tl1");
  }
};

构造函数是一个带有默认参数的模板。因此,您可以像往常一样调用它。但编译器必须等待您这样做,因为可能使用不同的模板参数。

这就是诀窍。只有在调用构造函数时,编译器才会评估静态断言。到那时,将有足够的信息。

第二个模板的工作方式相同:

template <typename Derived, typename Value>
class tl2
{
public:
  template <typename Defer = void>
  tl2()
  {
    static_assert(deferred_is_base_of<Defer, cl1, Value>::value,
                  "Value has to be derived from cl1");

    static_assert(deferred_is_base_of<Defer, tl2, Derived>::value,
                  "Derived has to be derived from tl2");
  }
};

class cl1 : tl1<cl1, cl2>
{
  cl1()
  {
  }
};

class cl2 : tl2<cl2, cl1>
{
  cl2()
  {
  }
};

int main() {}

推迟模板评估是TMP工具箱中的一个强大工具。

编辑: 正如@Brandon指出的那样,此处不需要延迟基数。只需将检查放入构造函数中即可推迟。