【问题标题】:Is it UB to reinterpret_cast the first union member to the type of the active union member?UB是否将第一个联合成员重新解释为活动联合成员的类型?
【发布时间】:2021-12-26 16:52:15
【问题描述】:

给定代码:

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
}

【问题讨论】:

  • 您问题中的具体代码可能是合法的,因为class.union.1 中的公共初始序列保证。您可能想在这些结构中添加一些东西...
  • @dratenik 如果我正确阅读了该段落,它允许通过c.b.x 访问c.a.x(假设它们的布局兼容)。所以它谈论的是工会成员,但我问的是工会成员本身。
  • 我会说第一个工会成员充当任何其他工会成员。我会说 reinterpret_cast 非活动工会成员是迂腐病态或 UB。
  • @Jarod42 并不意味着通过命名联合进行强制转换也将是 UB,例如auto&amp; b = reinterpret_cast&lt;B&amp;&gt;(c.myUnion)。我们在这里访问非活动成员吗?由于类和它们的第一个成员(此处忽略类型限制)之间的指针定义明确,我认为通过第一个成员访问它也可以。 Example
  • ub 还是不等什么?那里什么也抓不到

标签: c++ language-lawyer c++20


【解决方案1】:

根据“注释”部分中的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&lt;double*&gt;(p3); 取消引用 p4 肯定是 UB。您不能只在任何指针类型之间 reinterpret_cast (从技术上讲,您不能取消引用它们,但无论如何)。此外,虽然 p4 在你的情况下是一种 aliasing u.b,但读取它仍然是 UB,因为 u.b 不是工会的活跃成员。
  • 我知道cppreference 不是标准,但它可能是最好的免费参考,无需直接阅读标准。上面的示例是我链接到的页面的直接副本。按照标准,我不知道这个例子的有效性。
  • 紧随其后的那句话是 “执行一个类成员访问,指定一个非静态数据成员或一个非静态成员函数的泛左值实际上并不指定一个对象适当的类型 - 例如通过 reinterpret_cast 获得的类型 - 会导致未定义的行为”。所以是的,您可以强制转换它们,但由于我之前所说的,您仍然不能取消引用它们。
  • 而 OP 来自(非活动的)工会成员,而不是工会本身
  • 根据上面给出的引用,将指向联合中一个成员的指针重新解释为指向同一联合中另一个成员的指针是有效的,因为地址不会改变。在问题中,指向活动成员的指针被取消引用,因此关于取消引用指向非活动成员的指针的讨论没有实际意义。
猜你喜欢
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-28
相关资源
最近更新 更多