为什么向上转换有效,但向下转换会产生编译时错误
Why upcasting works but downcasting gives compile time error?
要使用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* pbob
的dynamic_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 必须是多态的,否则不需要多态类型。
- 在缺少函数重载时抛出异常,并带有 std::variant 而不是编译时错误
- 如何在常量计算表达式中获取编译时错误?
- C++根据调用的构造函数强制编译时错误
- 使用 std::iterator_traits<> 时编译时错误不明确
- is_same和variadic模板编译时错误无效转换
- 私有运营商删除会触发 GCC 和 Clang 的编译时错误,但不会在 MSVC 上触发编译时错误
- 运行时与编译时多态性:更好的可读性与编译时错误检查,更重要的是
- 引发编译时错误
- 为什么调用不明确的 ctor 时没有编译时错误?
- 在 sizeof 运算符上强制编译时错误
- 未经授权的私有类成员访问会产生编译时错误而不是运行时错误?
- 从Mac上的终端编译时C 错误
- 使用MINGW-W64使用-M32选项(32位代码)编译时错误
- C++线程错误 - 带有类参数的编译时错误
- 薄板样条形状转换运行时错误 [使用代码 -1073741819 退出]
- 如果编译时间构量参数是错误的,则生成编译时错误
- 尝试调用指向成员函数的函数指针时出现编译时错误
- 编译时错误:删除了联合默认构造函数
- 为什么向上转换有效,但向下转换会产生编译时错误
- 编译时错误:期望的构造函数、析构函数或转换