确保类型名称类型是“派生的基”

Make sure that typename type is a Derived of Base

本文关键字:类型 派生 派生的基 确保      更新时间:2023-10-16

我有这种代码

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 timeTcl2类型或derived from cl2Ccl1类型或derived from cl1。我需要确定 cl2 类型,否则getTbyName会一团糟。

谢谢你的时间
昆汀

您可以使用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>;
               ^

在线演示

<type_traits> 中的std::is_base_ofstd::enable_if结合使用:

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

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

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

从您后来的帖子来看,您还想检查您的 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所指出的,这里不需要递延基数。将检查放入构造函数以延迟到此处就足够了。