相关: 如何在Union中初始化非POD成员

标准说

联合的一个非静态数据成员最多可以有一个大括号或相等初始化器。

但是

struct Point {
    Point() {}
    Point(int x, int y): x_(x), y_(y) {}
    int x_, y_;
};

union U {
    int z;
    double w;
    Point p = Point(1,2);
};


#include <iostream>
int main () {
    U u;
    std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
}

印刷品 4196960:0 而不是预期的 1:2.

我认为这是一个编译器错误。是这样吗?

有帮助吗?

解决方案

C++11[类。ctor]/5个州:

A 违约情况 类的构造函数 X 是class的构造函数 X 这可以在没有参数的情况下调用。如果类没有用户声明的构造函数 X, ,没有参数的构造函数隐式声明为默认(8.4)。隐式声明的默认构造函数是 inline public 其类的成员。类的默认构造函数 X 被定义为删除,如果:

  • X 是一个类似union的类,它具有一个具有非平凡默认构造函数的variant成员,
  • 任何非静态数据成员,没有 大括号或相等初始化器 是引用类型,
  • const限定类型(或其数组)的任何非变体非静态数据成员,没有 大括号或相等初始化器 没有用户提供的默认构造函数,
  • X 是一个联合体,它的所有变体成员都是const限定类型(或其数组),
  • X 是一个非联合类,任何匿名联合成员的所有成员都是const限定类型(或其数组),
  • 任何直接或虚拟基类,或非静态数据成员没有 大括号或相等初始化器, ,具有类类型 M (或其阵列)和任 M 没有应用于 M默认构造函数导致歧义或从默认的默认构造函数中删除或无法访问的函数,或
  • 任何直接或虚拟基类或非静态数据成员都具有具有析构函数的类型,该析构函数从默认的默认构造函数中删除或无法访问。

如果默认构造函数不是用户提供的,并且如果:

  • 它的类没有虚函数(10.3)和虚基类(10.1),并且
  • 其类的非静态数据成员没有一个 大括号或相等初始化器, ,而
  • 其类的所有直接基类都具有微不足道的默认构造函数,并且
  • 对于类类型(或其数组)的类的所有非静态数据成员,每个此类都有一个简单的默认构造函数。

否则,默认构造函数为 非平凡.

由于结构 Point 在OP中有一个非平凡的默认构造函数,

Point() {}

包含类型成员的联合的默认构造函数 Point 应该 被定义为根据第一个子弹删除:

  • X 是一个类似union的类,它具有一个具有非平凡默认构造函数的variant成员

导致OP中呈现的程序形成不良。

然而,委员会似乎认为这是一个缺陷的情况下,一个联盟的成员有一个 大括号或相等初始化器,每 核心工作组第1623期:

根据12.1[类。ctor]第5段,

类X的默认构造函数定义为删除,如果:

  • X 是一个类似union的类,它具有一个具有非平凡默认构造函数的variant成员,

  • ...

  • X 是一个联合体,它的所有变体成员都是const限定类型(或其数组),

  • X 是一个非联合类,任何匿名联合成员的所有成员都是const限定类型(或其数组),

  • ...

因为非静态数据成员初始化器的存在在道德上等同于 mem-初始化器, ,可能应该修改这些规则,以便在联合成员具有非静态数据成员初始值设定项时将生成的构造函数定义为已删除。(注意9.5[class中的非规范性引用。联盟]第2-3和7.1.6.1段[dcl.type.cv如果改变这一限制,也需要更新第2款。)

将要求添加到9.5[class也会有所帮助。union]如果union的所有成员都具有const限定类型,则需要非静态数据成员初始值设定项或用户提供的构造函数。

在更一般的说明中,为什么默认构造函数被定义为删除,只是因为一个成员有一个非平凡的默认构造函数?联合本身不知道哪个成员是活动成员,默认构造不会初始化任何成员(假设没有 大括号或相等初始化器).由联合的"所有者"来控制活动成员的生存期(如果有的话),并且需要用户提供的构造函数正在强制一种没有意义的设计模式。沿着同样的路线,为什么默认析构函数被定义为删除,只是因为一个成员有一个非平凡的析构函数?我会同意这个限制,如果它只适用于联合也有一个用户提供的构造函数。

第1623期的地位是"起草",表明委员会认为这个问题可能是一个缺陷--否则为什么还要允许一个 大括号或相等初始化器 工会会员?-但尚未花时间确定决议的适当措辞。事实上,该段落在当前的C++14草案N3936([类。ctor]/4),除了措辞"任何直接或虚拟基类或非静态数据成员"到处被更简单的"任何潜在构造的子对象"取代。"

虽然两个编译器的行为并不严格符合,但我认为Clang是按照标准的精神行事的。看起来GCC会被删除的默认构造函数和 大括号或相等初始化器:

  • 确实如此 在没有 大括号或相等初始化器,
  • 大括号或相等初始化器 当前和最大警告GCC4.8.2根本不执行联合的初始化,并且 甚至警告说,成员未初始化使用:

    main.cpp: In function 'int main()':
    main.cpp:17:39: warning: 'u.U::p.Point::y_' is used uninitialized in this function [-Wuninitialized]
         std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
                                           ^
    main.cpp:17:22: warning: 'u.U::p.Point::x_' is used uninitialized in this function [-Wuninitialized]
         std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
                          ^
    

GCC可能应该符合标准并将程序诊断为格式错误,或者模仿clang的行为并从 大括号或相等初始化器.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top