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

bur*_*nck 1 c++ inheritance static-cast upcasting

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

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

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

  • 一个都没有?访问static_castfrom void 指针是未定义的行为,除非该指针确实指向确切的类型。
  • 对于所有没有多重继承的链?然后,编译器可以保证Base始终位于Derived- 但标准怎么说?
  • 对于所有链,Base在所有中间多重继承链的第一个父类中可以找到什么?Base也许和的开始Derived仍然匹配?
  • 总是?static_castto void 指针始终可以调整到第一个父级的开头,而static_castfrom void 指针则撤消该调整。但对于多重继承,“第一个父母”不一定是所有父母的父母。

Sto*_*ica 5

static_cast<Base*>(static_cast<void*>(derived))在 C++ 标准中有一个名称。它被称为reinterpret_cast. 它在[expr.reinterpret.cast] 第 7 段中指定:

\n\n
\n

对象指针可以显式转换为不同类型的对象指针。当对象指针类型的纯右值 v 转换为对象指针类型 \xe2\x80\x9cpointer to cv T\xe2\x80\x9d 时,结果为static_\xc2\xadcast<cv T*>(static_\xc2\xadcast<cv void*>(v))。[ 注意:将 \xe2\x80\x9c 类型的指针到 T1\xe2\x80\x9d 的纯右值转换为 \xe2\x80\x9c 类型的指针到 T2\xe2\x80\x9d (其中 T1 和 T2 是对象类型,其中T2 的对齐要求并不比 T1 更严格),并且返回到其原始类型会产生原始指针值。\xe2\x80\x94 尾注]

\n
\n\n

Areinterpret_cast是我们告诉编译器将指针视为其他东西。编译器可以或不会根据该指令进行任何调整。如果我们撒谎了,那么这种行为就是不确定的。该标准是否规定了此类何时reinterpret_cast有效?事实上确实如此。[basic.compound] 第 4 段定义了指针互换性的概念:

\n\n
\n

两个对象 a 和 b 是指针可相互转换的,如果:

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

如果两个对象是指针可相互转换的,那么它们具有相同的地址,并且可以通过reinterpret_\xc2\xadcast. [ 注意:数组对象及其第一个元素不可进行指针互换,即使它们具有相同的地址。\xe2\x80\x94 尾注]

\n
\n\n

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

\n