将第一个联合成员重新解释为活动联合成员的类型是否是未定义行为(UB)?

3

给定以下代码:

struct A { int i; };
struct B { std::string s; };

struct C
{
    union
    {
        A a{};
        B b;
    };
};

以下访问是否定义良好:
void foo()
{
    C c;        // c.a is active
    c.b = {};   // now c.b is active

    auto& b = reinterpret_cast<B&>(c.a); // read c.b via access to c.a
    // use b somehow
}

1
你问题中的特定代码可能是合法的,因为class.union.1中有常见初始序列保证。你可能想在这些结构体中放置一些内容... - user14215102
我会说第一个union-member的行为就像任何其他的union-member一样。而且我认为reinterpret_cast无效的union member在语言上是不正确或未定义的。 - Jarod42
你从这里期待什么?没有任何东西可以让你得到。 - Алексей Неудачин
我认为 reinterpret_cast<B&>(c.myUnion) 是可以的,而 reinterpret_cast<B&>(c.myUnion.a) 不行。我通常使用 constexpr 函数来检查 UB,但不幸的是,reinterpret_cast 不是 constexpr。 :/ - Jarod42
你可能想在问题中添加使用(取消引用)指针。标准允许许多指针到指针的转换,这些转换不会产生可以合法取消引用的指针。 - Richard Critten
显示剩余4条评论
1个回答

0
根据cppreference中的“备注”部分,对于联合体的一个成员指针重新解释为另一个成员类型的指针是完全合法的。

假设对齐要求已满足,reinterpret_cast 不会改变指针的值,除了涉及指针相互转换的对象的少数有限情况:

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // not standard-layout
union U { int a; double b; } u = {0};
int arr[2];

int* p1 = reinterpret_cast<int*>(&s1); // value of p1 is "pointer to s1.a" because s1.a
                                   // and s1 are pointer-interconvertible

int* p2 = reinterpret_cast<int*>(&s2); // value of p2 is unchanged by reinterpret_cast and
                                   // is "pointer to s2". 

int* p3 = reinterpret_cast<int*>(&u);  // value of p3 is "pointer to u.a": u.a and u are
                                   // pointer-interconvertible

double* p4 = reinterpret_cast<double*>(p3); // value of p4 is "pointer to u.b": u.a and u.b
                                        // are pointer-interconvertible because both
                                        // are pointer-interconvertible with u

int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast and
                                    // is "pointer to arr"

double* p4 = reinterpret_cast<double*>(p3);p4 进行解引用绝对会导致未定义行为。你不能仅仅使用reinterpret_cast来转换任意指针类型(严格来说无法解引用它们,但不管怎样)。此外,虽然在您的情况下 p4 有一些“别名”关系到 u.b,但读取它仍然是未定义行为,因为 u.b 不是联合中的活动成员。 - Timo
我知道cppreference不是标准,但它可能是最好的免费参考资料,而不必直接阅读标准。上面的示例是我链接页面的直接复制。我不知道这个示例在标准中的有效性。 - Mestkon
紧接着的那句话说:“在指定非静态数据成员或非静态成员函数的类成员访问中,如果 glvalue 实际上并未指定适当类型的对象(例如通过 reinterpret_cast 获得的对象),则会导致未定义的行为。”所以是的,你可以进行强制转换,但由于我之前所说的原因,你仍然不能对它们进行解引用。 - Timo
从(非活动的)联合成员转换,而不是从联合本身转换 - Jarod42
根据上面给出的引用,将联合中一个成员的指针重新解释为同一联合中另一个成员的指针是有效的,因为地址不会改变。在问题中,对活动成员的指针进行了取消引用,因此讨论取消引用非活动成员的指针是无意义的。 - Mestkon
上述引用中唯一未指定的是在执行reinterpret_cast之前访问c.a的方式。但从技术上讲,没有访问存储在c.a中的值,只有其地址。 - Mestkon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接