你可以在具有相同表示的类型之间进行reinterpret_cast吗?

Dr.*_*Gut 5 c++ strict-aliasing undefined-behavior language-lawyer reinterpret-cast

假设我们有两种类型,它们具有相同的表示形式(相同的成员变量和基类,顺序相同)。它们之间是否有效(即不是 UB)reinterpret_cast?例如,reinterpret_castMary到有效吗Ashley&?如果这两种类型是多态的怎么办?

struct Mary {
    int  m1;
    char m2;
};

struct Ashley {
    int  a1;
    char a2;
};

int TryTwins ()
{
    Mary mary = {};

    Ashley& ashley = reinterpret_cast<Ashley&> (mary);
    ashley.a1 = 1;
    ashley.a2 = 2;

    return mary.m1 + mary.m2;
}
Run Code Online (Sandbox Code Playgroud)

如果我们知道源类型以目标类型的成员变量开头,那么我们将对象的开头强制转换为另一种类型会怎样?例如,这有效吗(即不是 UB)?

struct Locomotive {
    int    engine;
    char   pantograph;
};

struct Train {
    int    engine;
    char   pantograph;
    int*   wagon1;
    int**  wagon2;
    int*** wagon3;
};

int TryTrain ()
{
    Train train = {};

    Locomotive& loc = reinterpret_cast<Locomotive&> (train);
    loc.engine     = 1;
    loc.pantograph = 2;

    return train.engine + train.pantograph;
}
Run Code Online (Sandbox Code Playgroud)

请注意,所有主要编译器都将这些视为有效的强制转换(现场演示)。问题是,C++语言是否允许这样做。

L. *_* F. 2

[expr.reinterpret.cast]/11 :

\n\n
\n

T1如果可以显式转换\xe2\x80 T2\x9c 类型的表达式,则可以将 \xe2\x80\x9creference\n 类型的泛左值表达式强制T1转换为 \xe2\x80\x9d 类型的指针到 \xe2\x80\x9d指向类型 \xe2\x80\x9cT2使用 指向 \xe2\x80\x9d 的指针reinterpret_\xc2\xadcast。\n 结果引用与源泛左值相同的对象,但具有指定的类型。[...]

\n
\n\n

MaryAshley是对象类型,因此指向它们的指针可以相互转换。现在,我们可以使用类型的左值Ashley来访问底层Mary对象。

\n\n

[基本.lval]/8 :

\n\n
\n

如果程序尝试通过以下类型之一以外的泛左值访问对象的存储值,则行为未定义:

\n\n
    \n
  • 对象的动态类型,

  • \n
  • 对象动态类型的 cv 限定版本,

  • \n
  • 类似于对象的动态类型的类型,

  • \n
  • 与对象的动态类型相对应的有符号或无符号类型,

  • \n
  • 与对象动态类型的 cv 限定版本相对应的有符号或无符号类型,

  • \n
  • 聚合或联合类型,其元素或非静态数据成员中包含上述类型之一(递归地包括子聚合或包含的联合的元素或非静态数据成员),

  • \n
  • 是对象动态类型的(可能是 cv 限定的)基类类型的类型,

  • \n
  • a charunsigned char、 或std\xe2\x80\x8b::\xe2\x80\x8bbyte类型。

  • \n
\n
\n\n

这些都不涵盖所讨论的情况。(“类似”谈论简历资格。)因此,未定义的行为。

\n

  • 我同意,尽管可以说“ashley.a1”是“int”类型的左值,它用于访问“mary.m1”对象,而“mary.m1”是“int”类型的对象。显然,一种常见的解释是表达式“ashley.a1”访问“ashley.a1”所属的整个对象的所有存储值,尽管标准没有明确说明这一点 (3认同)