C++ 如何在CRTP中实现编译时检查downcast是否有效?
我有一个普通的旧CRPT(请不要被访问限制分心-问题不是关于它们):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
模板
阶级基础{
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