C++ 如何在CRTP中实现编译时检查downcast是否有效?

C++ 如何在CRTP中实现编译时检查downcast是否有效?,c++,casting,crtp,static-cast,upcasting,C++,Casting,Crtp,Static Cast,Upcasting,我有一个普通的旧CRPT(请不要被访问限制分心-问题不是关于它们): 模板 阶级基础{ void MethodToOverride() { //一般的东西在这里 } void problematicsmethod() { 静态_cast(this)->MethodToOverride(); } }; 这与通常使用的方式一样: class ConcreteDerived : public Base<ConcreteDerived> { void MethodToOver

我有一个普通的旧CRPT(请不要被访问限制分心-问题不是关于它们):

模板
阶级基础{
void MethodToOverride()
{
//一般的东西在这里
}
void problematicsmethod()
{
静态_cast(this)->MethodToOverride();
} 
};
这与通常使用的方式一样:

 class ConcreteDerived : public Base<ConcreteDerived> {
     void MethodToOverride()
     {
        //custom stuff here, then maybe
        Base::MethodToOverride();
     }
 };
类:公共基{
void MethodToOverride()
{
//这里有定制的东西,也许吧
Base::MethodToOverride();
}
};
现在,静态的演员阵容困扰着我。我需要向下转换(而不是向上转换),所以我必须使用显式转换。在所有合理的情况下,强制转换都是有效的,因为当前对象确实属于派生类

但是如果我以某种方式改变了层次结构,演员阵容现在变得无效了呢


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

在编译时,您只能检查静态类型,而这正是
静态向下转换
所做的


给定一个
Base*
,它在运行时只能知道它的动态类型是什么,也就是说,它是否实际指向
ConcreteDerived
或其他对象。因此,如果您想检查这一点,必须在运行时完成(例如,通过使用
dynamic\u cast

为了额外的安全性,您可以向Base添加一个受保护的构造函数,以确保从中派生出某些内容。那么唯一的问题就是那些真正愚蠢的人:

class ConcreteDerived : public Base<SomeOtherClass>
类:公共基

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

要扩展@Bo Persson所说的内容,您可以使用Boost.TypeTraits或C++0x/11
在所述构造函数中执行编译时检查:

#包括
模板
结构基{
typedef基MyType;
Base(){
typedef char ERROR_你搞砸了[std::is_base_of::value?1:-1];
}
};
类:公共基{
};
int main(){
混凝土衍生cd;
}

完整示例。

当您执行以下操作时:

struct ConcreteDerived : public Base<Other>  // Other was not inteded
struct-ConcreteDerived:public-Base//Other未插入
您可以创建
(派生或基本)的对象。但是如果您尝试调用该函数,它只会给出与
static\u cast
相关的编译错误。IMHO将满足所有实际场景


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

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

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

通过使所有基构造函数私有,我们可以防止来自基的不希望的继承

通过将派生声明为基的友元,我们只允许CRPT所期望的继承

在此之后,CRPT downcast应该是正确的(因为有些东西是从基继承的,而这个“东西”可能只是派生的,而不是其他类)


也许出于实际目的,第一步(使基础抽象化)是多余的,因为派生的成功静态强制转换保证在基础层次结构的某个地方。如果派生是从
Base
(正如CRPT所期望的)继承的,但同时派生在派生代码的某个地方创建
Base
(无继承)的另一个实例(它可以,因为它是朋友),则这只允许出现异常错误。但是,我怀疑是否有人会意外地编写这种奇特的代码。

您不需要在基类中使用MethodToOverride。@ysdx:如果我希望它可以选择性地重写,或者有一些通用的实现,我需要它。但是如果您在基类中有函数,调用将始终“起作用”,因为有一个函数要调用。@Bo Persson:是的,这就是为什么我需要一个向下转换来调用派生度最高的函数,而派生度最高的函数可以调用基础版本(如果需要的话)。你不考虑使用虚拟函数吗?:-)但是要使用dynamic_cast,您需要一些虚拟函数,我相信这个构造试图避免这些函数。true。我的观点是,这个检查只能在运行时执行,这会带来相关的成本,正如你所指出的,你能不能用“你搞砸了”替换F字,这样就不会有大量的选票和旗帜落在你身上?这里没有什么问题,问题不仅在于确保参数
Derived
有效地派生自
Base
,还在于
this
的当前值有效地派生自
Derived
(或其派生类型),您只能在运行时进行检查。@Matthieu:我的答案是@Bo Personn的扩展,所以他所说的受保护的构造函数对我也适用。然后,如果
this
不是派生的东西,那么就有非常非常错误的东西。我同意在CRTP
中,this
应该是可以的,但问题是要找到一种方法来检测它何时不是。不幸的是,这没有抓住从同一CRTP基派生的两个类的情况,其中一个错误地传递另一个,例如
类ConcreteDerived1:public Base{};类ConcreteDerived2:公共基{}。更糟糕的是,
static\u cast
Derived
仍然可以编译,因为编译器“知道如何”进行向下转换。
#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;
}
struct ConcreteDerived : public Base<Other>  // Other was not inteded