如何在CRTP中实现向下转换是否有效的编译时检查

How to implement a compile-time check that a downcast is valid in a CRTP?

本文关键字:有效 是否 编译 检查 转换 CRTP 实现      更新时间:2023-10-16

我有一个普通的旧CRPT(请不要被访问限制分散注意力-问题不在于它们):

 template<class Derived>
 class Base {
     void MethodToOverride()
     {
        // generic stuff here
     }
     void ProblematicMethod()
     {
         static_cast<Derived*>(this)->MethodToOverride();
     } 
 };

通常是这样使用的:

 class ConcreteDerived : public Base<ConcreteDerived> {
     void MethodToOverride()
     {
        //custom stuff here, then maybe
        Base::MethodToOverride();
     }
 };

现在static_cast困扰着我。我需要向下强制转换(而不是向上强制转换),所以我必须使用显式强制转换。在所有合理的情况下,强制转换都是有效的,因为当前对象确实属于派生类。

但是,如果我以某种方式改变了层次结构,强制转换现在变得无效怎么办?

在这种情况下,我是否可以强制执行一个编译时检查,以确保显式向下转换是有效的?

在编译时你只能检查静态类型,这就是static_cast已经做的。

给定一个Base*,它只能并且只能在运行时知道它的动态类型是什么,也就是说,它是否实际上指向ConcreteDerived或其他东西。所以如果你想检查这个,它必须在运行时完成(例如通过使用dynamic_cast)

为了更安全,您可以向Base添加一个受保护的构造函数,以确保某些是从它派生的。那么唯一的问题就是对于那些真正愚蠢的人:

class ConcreteDerived : public Base<SomeOtherClass>

但是这应该在第一次代码审查或测试用例中被捕获。

要扩展@Bo Persson所说的内容,您可以在所述构造函数中执行编译时检查,例如使用Boost。TypeTraits or c++ 0x/11 <type_traits>:

#include <type_traits>
template<class Derived>
struct Base{
  typedef Base<Derived> MyType;
  Base(){
    typedef char ERROR_You_screwed_up[ std::is_base_of<MyType,Derived>::value ? 1 : -1 ];
  }
};
class ConcreteDerived : public Base<int>{
};
int main(){
  ConcreteDerived cd;
}

似乎存在一种在编译时检查CRPT正确性的方法。

通过使Base抽象(向Base添加一些纯虚方法),我们保证任何Base实例都是某个派生实例的一部分。

通过将所有Base构造函数设置为私有,可以防止Base的不良继承。

通过声明Derived为Base的友类,我们允许CRPT所期望的唯一继承。

在此之后,CRPT向下转换应该是正确的(因为某些东西是从基类继承的,并且这个"某些东西"可能只是派生的,而不是其他类)

也许出于实际目的,第一步(使Base抽象)是多余的,因为成功的static_cast保证Derived在Base层次结构中的某个位置。如果Derived继承了Base <Derived> (正如CRPT所期望的那样),但同时Derived在派生代码的某个地方创建了Base <derived> 的另一个实例(没有继承)(它可以,因为它是友元),则只允许出现外来错误。然而,我怀疑是否有人会不小心写出如此奇特的代码。

当你做如下操作时:

struct ConcreteDerived : public Base<Other>  // Other was not inteded

可以创建class的对象(派生或基类)。但是如果您尝试调用该函数,它只会给出与static_cast相关的编译错误。我认为它将满足所有实际情况。

如果我正确理解了这个问题,那么我觉得答案就在你的问题本身。:)