指向派生类的指针中的"static_cast<Base*>(static_cast<void*>(派生))"何时有效?

When is a `static_cast<Base*>(static_cast<void*>(derived))` from a pointer to a derived class valid?

本文关键字:派生 cast gt lt static 有效 void 何时 指针 Base      更新时间:2023-10-16

对于这个问题,不涉及多态性,即没有虚拟方法,没有虚拟基类。以防万一,我的案子不涉及其中任何一个。

假设我有一个类Derived,它有一个明确的可访问父级类型Base,没有多态性(没有虚拟方法,没有虚拟基类(,但可能涉及间接和/或多重继承。 进一步假设我有一个有效的指针Derived *derived(指向类型Derived的对象或其子类(。

在这种情况下,我相信static_cast<Base*>(derived)是有效的 (生成有效的可用指针(。当BaseDerived之间的祖先链涉及多重继承时,此static_cast可能意味着指针调整以在Derived实例中定位Base实例。为此,编译器需要知道继承链,在这种情况下他就是这样做的。但是,如果插入了void *的中间强制转换,则该继承链信息将对编译器隐藏。对于哪个继承链,这样的静态强制转换仍然有效?我期望以下其中一项:

  • 完全没有?从 void 指针访问static_cast是未定义的行为,除非指针确实指向确切的类型。
  • 对于所有没有多重继承的链?然后,编译器可以保证Base始终处于Derived的开头 - 但是标准是什么?
  • 对于在所有中间多重继承链的第一个父类中找到Base的所有链?也许BaseDerived的开始仍然匹配?
  • 总是?static_castvoid 指针始终可以调整到第一个父指针的开头,并且从 void 指针撤消该调整static_cast。但是,对于多重继承,"第一父母"不一定是所有父母的父母。
static_cast<Base*>(static_cast<void*>(derived))

在C++标准中有一个名称。这被称为reinterpret_cast。在[expr.reinterpret.cast]第7段中指定:

对象指针可以显式转换为不同类型的对象指针。当对象指针类型的 prvalue v 转换为对象指针类型"指向 cv T"时,结果为static_­cast<cv T*>(static_­cast<cv void*>(v))。[ 注意:将类型为"指向 T1 的指针"的 prvalue 转换为"指向 T2 的指针"类型(其中 T1 和 T2 是对象类型,其中 T2 的对齐要求不比 T1 的对齐要求更严格(并返回其原始类型将生成原始指针值。

reinterpret_cast是我们告诉编译器将指针视为其他东西。编译器在此指令下可以或将要执行任何调整。如果我们撒谎,那么行为简直是不确定的。标准是否规定这样的reinterpret_cast何时有效?实际上确实如此。在[basic.compound]第4段中定义了指针互转换性的概念:

在以下情况下,两个对象 a 和 b 是指针可相互转换的:

  • 它们是同一对象,或者
  • 一个是联合对象,另一个是该对象的非静态数据成员([class.union](,或者
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有 非静态数据成员,该对象的任何基类子对象 ([class.mem](,或
  • 存在一个对象 C,使得 A 和 C 是指针可相互转换的,C 和 B 是指针可相互转换的。

如果两个对象是指针可相互转换的,则它们具有相同的 地址,并且可以从指针获取指向 1 的指针 通过reinterpret_­cast到另一个。[ 注意:数组对象及其 第一个元素不是指针可相互转换的,即使它们具有 相同的地址。— 尾注 ]

第三个项目符号是你的答案。类层次结构中的对象必须遵守限制(从顶部基础到最派生的标准布局(,只有这样才能保证强制转换给出明确定义的结果。