布局兼容类型的并集

时间:2018-10-29 17:45:34

标签: c++ language-lawyer c++17 unions standard-layout

看下面的代码:

struct A {
    short s;
    int i;
};
struct B {
    short s;
    int i;
};

union U {
    A a;
    B b;
};

int fn() {
    U u;
    u.a.i = 1;
    return u.b.i;
}

是否保证fn()返回1

注意:这是this的后续问题。

1 个答案:

答案 0 :(得分:10)

是的,这是定义的行为。首先,让我们看看标准对AB的看法。 [class.prop]/3

  

如果满足以下条件,则S类为标准布局类:

     
      
  • 没有非标准布局类(或此类数组)或引用的非静态数据成员,
  •   
  • 没有虚拟函数,也没有虚拟基类,
  •   
  • 对所有非静态数据成员具有相同的访问控制,
  •   
  • 没有非标准布局的基类,
  •   
  • 最多具有一个任何给定类型的基类子对象,
  •   
  • 具有该类及其基类中所有的非静态数据成员和位字段,并且首先在同一类中声明它们,并且
  •   
  • [...](在这种情况下,这里什么也没说)
  •   

因此AB都是标准布局类型。如果我们看[class.mem]/23

  

如果两个标准布局结构类型的公共初始序列包含两个类的所有成员和位字段([basic.types]),则它们是布局兼容的类。

[class.mem]/22

  

两种标准布局结构类型的公共初始序列是非静态数据成员和位字段的最长序列(按声明顺序),从每个结构中的第一个此类实体开始,以便相应的实体具有布局兼容类型,要么两个实体都使用no_unique_address属性([dcl.attr.nouniqueaddr]声明),要么都不声明,并且两个实体都是具有相同宽度的位域,或者都不是位域。

[class.mem]/25

  

在具有结构类型T1的活动成员的标准布局联合中,允许读取结构类型T2的另一个联合成员的非静态数据成员m,条件是m是T1的公共初始序列的一部分,并且T2;行为就像提名了T1的相应成员一样。 [示例:

struct T1 { int a, b; };
struct T2 { int c; double d; };
union U { T1 t1; T2 t2; };
int f() {
  U u = { { 1, 2 } };   // active member is t1
  return u.t2.c;        // OK, as if u.t1.a were nominated
}
     

-示例示例[注]:通过非易失性类型的glvalue读取易失性对象具有未定义的行为([dcl.type.cv])。 —尾注]

这时,我们得到的类具有相同的公共初始序列,布局相同,并且将访问非活动类型的相同成员视为访问活动类型的成员。