为什么向上转换有效,但向下转换会产生编译时错误

Why upcasting works but downcasting gives compile time error?

本文关键字:转换 编译时错误 有效 为什么      更新时间:2023-10-16

要使用dynamic_casting,继承层次结构必须是多态的。但是在下面的代码段中,dynamic_cast完美地用于上级转换,而在编译时失败以进行向下转换?上投有效的原因是什么?

class B{};
class C : public B{};
int main()
{
    C* pcob = new C();
    B* bp = dynamic_cast<B*>(pcob);     // #1 : Upcasting works
    if(bp)
        cout << "casted" << endl;
    else
        cout << "bp null" << endl;
    delete bp;    
    B* pbob = new B();
    C* pc = dynamic_cast<C*>(pbob);     // #2 : Downcasting gives error
    if(pc)
        cout << "casted" << endl;
    else
        cout << "pc null" << endl;
    delete pbob;
    return 0;
}

#2 处的编译时错误是

main.cpp: In function ‘int main()’:
main.cpp:36:34: error: cannot dynamic_cast ‘pbob’ (of type ‘class B*’) to type ‘class C*’ (source type is not polymorphic)
 C* pc = dynamic_cast<C*>(pbob);

B至少需要一个virtual方法。否则类型不是多态的,dynamic_cast不能应用于下铸。前一种情况是隐式类型转换

但dynamic_cast是明确使用的。编译器如何忽略它?

从标准 (5.2.7/5(

如果 T 是"指向 cv1 B 的指针",

并且 v 的类型为"指向 cv2 D 的指针",使得 B 是 D 的基类,则结果为 指向 v 指向的 D 对象的唯一 B 子对象的指针。同样,如果 T 是"对 cv1 B 的引用"并且 v 的类型为 cv2 D,使得 B 是 D 的基类,结果是所引用的 D 对象的唯一 B 子对象 到 by v. 68 如果 T 是左值引用,则结果为左值;如果 T 是右值引用,则结果为 x值。在 无论是指针情况还是引用情况,如果 CV2 的 CV 限定性大于 CV1,或者 B 是 D 的不可访问或不明确的基类,则程序的格式不正确。

struct B { };
struct D : B { };
void foo(D* dp) {
  B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}

—结束示例 ]

标准被引用 - 赞美是。

要了解为什么标准指定了它的行为,它有助于考虑您要求编译器做什么以及它与运行时类型信息的常见实现的关系。

您的dynamic_cast<B*>(pcob)只需要知道B相对于包含C的偏移量 - 对于每个C对象来说,这是一个恒定量,即使该C嵌入到其他对象中也是如此。 在运行时之前,不会依赖任何未知的东西。

相比之下,B* pbobdynamic_cast<C*>(pbob) - 编译器通常无法知道指向的B是否嵌入在另一个对象中,更不用说该对象是否碰巧是 - 或者也派生自 - C,以便强制转换可以成功。 通常,它需要依赖于仅为多态类型生成的运行时类型信息。

在您的特定代码中,编译器知道所有对象的实际类型,并且只需一点努力就可以支持向下转换,但是获取B*参数或调用一些返回B*的不透明工厂方法的函数中的类似代码有时可能会与包含C的运行时对象dynamic_cast,而有时则不会 - 编译器不能使用任何实际类型的"本地化"知识。 在本地使用中,您可以轻松创建指向实际运行时类型的指针,因此要求编译器支持"本地化"用法没有任何效用,并且标准只是批量禁止此类强制转换。

  • #1 有效 - C 具有 B 和其他一些功能的所有功能
  • #2 失败 - B 不具备 C 的所有功能

集合论真的

dynamic_cast不需要

多态类型进行向上转换。

来自 5.2.7/1 的一些基础知识 [expr.dynamic.cast]

表达式 dynamic_cast(v( 的结果是将表达式 v 转换为类型 T 的结果。

但是,这不包括为什么类型不必是多态的。为了更好地理解 5.2.7/5 和 5.2.7/6 中的以下引文,请稍微澄清一下。

如果 T 是"指向 cv1 B 的指针",

并且 v 的类型是"指向 cv2 D 的指针",使得 B 是 D 的基类,则结果是指向 v 指向的 D 对象的唯一 B 子对象的指针。

struct B { };
struct D : B { };
void foo(D* dp) {
    B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}

上面提供了使用兼容的 cv 限定符(常量易失性(从 D 向上转换为 B 的特定实例。

下一部分 5.2.7/6 将进一步阐明为什么源类型不必是多态的

否则,v 应是指向多态类型的指针或左值。

这里的关键是使用"否则"。从另一个方向看,如果 D 不能隐式转换为 B,则 D 必须是多态的,否则不需要多态类型。