【问题标题】:Aggregate initialization of a union in C++ with `{}`使用 `{}` 在 C++ 中聚合初始化联合
【发布时间】:2021-10-23 03:26:44
【问题描述】:

在下面的程序中,联合 U 有两个字段 a 和 b,每个字段都有不同的默认值。如果使用聚合初始化创建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时,你正在做value initialization。而且由于U不是类类型,所以会导致u成为zero initialized。
  • 令人惊讶的是编译器在这里出现分歧:Clang 打印 1 和 GCC 打印 0 - 似乎有缺陷,应该是 0
  • @Someprogrammerdude U 是一个类类型,它是联合聚合类。注意规范性术语“non-union aggregate”,例如在 [dcl.init.aggr]/5 中使用。因此,这是聚合初始化,并且 [dcl.init.aggr]/5 适用(/5.5 表示 U u{};,然后 /5.1 表示联合的第一个数据成员 a)。

标签: c++ language-lawyer unions aggregate-initialization


【解决方案1】:

Clang 正确,GCC 错误

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

聚合是一个数组或一个类([class])

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

A、B 和 U 都是聚合类,虽然前面是 非联合 聚合类,前者不符合条件。

根据[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
  • @MarcinPoloczek 我通常对 MSVC 在 GCC 和 Clang 不同意的语言极端情况下的行为没有什么价值。在这种特殊情况下,afaict Clang 正在按照标准实现正确的行为。
  • 是的,您的答案似乎是正确的。我错过了细节。我已经删除了我的帖子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-01
  • 2017-03-24
  • 2014-03-23
  • 2012-07-18
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多