Quu*_*one 12 c++ polymorphism dynamic-cast final
考虑这个类层次结构:
struct Animal { virtual ~Animal(); };
struct Cat : virtual Animal {};
struct Dog final : virtual Animal {};
Run Code Online (Sandbox Code Playgroud)
我的理解是,将final在class Dog确保没有人能够创建一个类继承Dog,其中,推论,意味着没有人能够创建IS-A类Dog和IS-A Cat同时进行.
考虑这两个dynamic_cast:
Dog *to_final(Cat *c) {
return dynamic_cast<Dog*>(c);
}
Cat *from_final(Dog *d) {
return dynamic_cast<Cat*>(d);
}
Run Code Online (Sandbox Code Playgroud)
GCC,ICC和MSVC忽略final限定符并生成调用__dynamic_cast; 这是不幸的,但并不令人惊讶.
让我感到惊讶的是,Clang和Zapcc都为("总是返回nullptr")生成了最佳代码from_final,但是生成了对__dynamic_castfor 的调用to_final.
这真的是一个错过的优化机会(在一个编译器中显然有人投入一些努力来尊重final演员阵容中的限定符),或者在这种情况下,由于某些微妙的原因,我仍然没有看到优化是不可能的?
好吧,我翻遍了 Clang 的源代码找到了这个方法:
/// isAlwaysNull - Return whether the result of the dynamic_cast is proven
/// to always be null. For example:
///
/// struct A { };
/// struct B final : A { };
/// struct C { };
///
/// C *f(B* b) { return dynamic_cast<C*>(b); }
bool CXXDynamicCastExpr::isAlwaysNull() const
{
QualType SrcType = getSubExpr()->getType();
QualType DestType = getType();
if (const PointerType *SrcPTy = SrcType->getAs<PointerType>()) {
SrcType = SrcPTy->getPointeeType();
DestType = DestType->castAs<PointerType>()->getPointeeType();
}
if (DestType->isVoidType()) // always allow cast to void*
return false;
const CXXRecordDecl *SrcRD =
cast<CXXRecordDecl>(SrcType->castAs<RecordType>()->getDecl());
//********************************************************************
if (!SrcRD->hasAttr<FinalAttr>()) // here we check for Final Attribute
return false; // returns false for Cat
//********************************************************************
const CXXRecordDecl *DestRD =
cast<CXXRecordDecl>(DestType->castAs<RecordType>()->getDecl());
return !DestRD->isDerivedFrom(SrcRD); // search ancestor types
}
Run Code Online (Sandbox Code Playgroud)
我对解析代码感到有点厌倦,但在我看来,您的值from_final不仅总是为空,因为最终属性,而且还因为它Cat不在Dog派生记录链中。诚然,如果它没有该final属性,那么它会提前退出(就像Cat是 Src 时那样),但它不一定总是 null。
我猜这有几个原因。第一个是dynamic_cast 在记录链上向上和向下进行转换。当 Source Record 具有 Final 属性时,您可以简单地检查链是否 Dest 是祖先(因为 Source 不能有派生类)。
但如果该课程不是最终课程怎么办?我怀疑可能还有更多的事情。也许多重继承使得搜索向上强制转换比向下强制转换更困难?在不停止调试器中的代码的情况下,我所能做的就是推测。
我确实知道:这 isAlwaysNull是一个提前退出的功能。这是一个合理的断言,它试图证明结果始终为空。你无法证明消极的一面(正如他们所说),但你可以反驳积极的一面。
也许您可以检查该文件的 Git 历史记录并向编写该函数的人发送电子邮件。(或者至少添加了final支票)。
| 归档时间: |
|
| 查看次数: |
397 次 |
| 最近记录: |