C ++中使用`{}`对联合体进行聚合初始化

6
在下面的程序中,联合体U有两个字段ab,每个字段都有不同的默认值。如果使用聚合初始化{}创建一个U类型的变量,那么联合体的值和活动成员是什么?
#include <iostream>

struct A { int x = 1; };
struct B { int x = 0; };

union U {
    A a;
    B b;
};

int main() {
    U u{};
    std::cout << u.a.x;
}

令人惊讶的是编译器在这里出现了分歧:Clang打印1,而GCC打印0,演示:https://gcc.godbolt.org/z/8Tj4Y1Pv1

其中一个编译器存在错误吗?还是这里的行为不受标准定义?


当您初始化 u 时,您正在执行 值初始化。由于 U 不是类类型,因此会导致 u零初始化 - Some programmer dude
令人惊讶的是编译器在这里出现了分歧:Clang打印1,而GCC打印0 - 看起来像是缺陷,应该是0。 - Marcin Poloczek
1
@Someprogrammerdude,U 是一个类类型,它是联合聚合类。请注意规范术语“非联合聚合”,例如在 [dcl.init.aggr]/5 中使用。因此,这是聚合初始化,适用于 [dcl.init.aggr]/5(后跟 /5.1 以获取联合的第一个数据成员 a 的信息)对 U u{}; 进行初始化。 - dfrib
1个回答

6

Clang是正确的,GCC是错误的

根据[dcl.init.aggr]/1:

聚合体是一个数组或类([class]), 具有以下特点:

  • (1.1) 没有用户声明或继承构造函数([class.ctor]),
  • (1.2) 没有私有或受保护的直接非静态数据成员([class.access]),
  • (1.3) 没有虚函数([class.virtual]),以及
  • (1.4) 没有虚拟、私有或受保护的基类([class.mi])。

ABU都是聚合类,尽管前两者是非联合聚合类,而后者不符合聚合类的条件。

根据[dcl.init.aggr]/5 [强调我]:

对于非联合的聚合体,每个未明确初始化的元素都按以下方式进行初始化:
  • (5.1) 如果该元素具有默认成员初始化程序([class.mem]),则从该初始化程序初始化该元素。
  • (5.2) 否则,如果该元素不是引用,则从空初始化列表([dcl.init.list])中进行复制初始化。
  • (5.3) 否则,程序无效。

如果聚合体是联合体且初始化列表为空,则:

  • (5.4) 如果任何变体成员具有默认成员初始化程序,则从其默认成员初始化程序初始化该成员;
  • (5.5) 否则,从联合体的第一个成员(如果有的话)中使用空初始化列表进行复制初始化。
因此
U u{};

这是聚合初始化,其结果是联合类的第一个数据成员,即类型为A(非联合聚合类)的数据成员a从空初始化列表复制初始化。由于类型为A的单个数据成员x具有默认成员初始化项,则根据上述[dcl.init.aggr]/5.1,数据成员x通过其默认成员初始化项进行初始化。

因此,Clang是正确的,而GCC是错误的。


GCC错误报告


MSVC 也会打印 0。 - Marcin Poloczek
@MarcinPoloczek 我通常不太重视 MSVC 在语言边界情况下的行为,因为它与 GCC 和 Clang 的意见不一致。在这种特定情况下,据我所知,Clang 根据标准实现了正确的行为。 - dfrib
是的,你的答案似乎是正确的。我错过了那个细节。我已经删除了我的帖子。 - Marcin Poloczek

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