我有一个普通的旧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
困扰我了。我需要一个向下投射(不是向上投射),所以我必须使用一个明确的演员。在所有合理的情况下,强制转换都是有效的,因为当前对象确实是派生类。
但是如果我以某种方式改变了层次结构并且演员现在变得无效呢?
我可以以某种方式强制执行编译时检查,在这种情况下显式向下转发有效吗?
答案 0 :(得分:5)
在编译时你只能检查静态类型,这就是static_cast
已经做过的事情。
给定Base*
,它只是,并且只能在运行时知道它的动态类型是什么,也就是说它是否实际指向{{1} }} 或者是其他东西。因此,如果要检查此项,则必须在运行时完成(例如,使用ConcreteDerived
)
答案 1 :(得分:4)
为了更加安全,您可以向Base添加受保护的构造函数,以确保某些是从它派生的。那么唯一的问题就是真正的愚蠢:
class ConcreteDerived : public Base<SomeOtherClass>
但是应该通过第一次代码审查或测试用例来捕获。
答案 2 :(得分:3)
要扩展@Bo Persson所说的内容,你可以使用例如Boost.TypeTraits或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;
}
完整示例on Ideone。
答案 3 :(得分:2)
似乎存在一种在编译时检查CRPT正确性的方法。
通过使Base抽象(向Base添加一些纯虚方法),我们保证任何Base实例都是某个派生实例的一部分。
通过将所有Base构造函数设为私有,我们可以防止Base的不良继承。
通过声明Derived为Base的朋友,我们允许CRPT预期的唯一继承。
在此之后,CRPT downcast应该是正确的(因为某些东西是从base继承而且这个“某些东西”可能只是Derived,而不是其他类)
也许出于实际目的,第一步(制作Base摘要)是多余的,因为成功的static_cast保证Derived位于Base层次结构中的某个位置。如果Derived与Base <Derived>
一起(如CRPT所期望的那样),这只允许奇怪的错误,但同时Derived在派生代码中的某处创建了Base <derived>
(没有继承)的另一个实例(它可以,因为它是朋友)。但是,我怀疑有人可能会不小心写出这种异国情调的代码。
答案 4 :(得分:1)
当您执行以下操作时:
struct ConcreteDerived : public Base<Other> // Other was not inteded
您可以创建class
(派生或基础)的对象。但是如果你尝试调用该函数,它只会提供与static_cast
相关的编译错误。恕我直言,它将满足所有实际情况。
如果我正确地理解了这个问题,那么我觉得答案就在你的问题中。 :)