相关:How to initialize a non-POD member in Union
Lý thuyết chuẩn
At most one non-static data member of a union may have a brace-or-equal-initializer.
Nhưng
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
int chính () {
U u;
std::cout << u.p.x_ << ":" << u.p.y_ << std::endl;
}
打印 4196960:0
而不是预期的 1:2
。
我认为这是一个编译器错误。是这样吗?
C++11 [class.ctor]/5 个状态:
MỘT mặc định constructor for a class X
is a constructor of class X
that can be called without an argument. If there is no user-declared constructor for class X
, a constructor having no parameters is implicitly declared as defaulted (8.4). An implicitly-declared default constructor is an inline public
member of its class. A defaulted default constructor for class X
is defined as deleted if:
X
is a union-like class that has a variant member with a non-trivial default constructor,
- any non-static data member with no brace-or-equal-initializer is of reference type,
- any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a user-provided default constructor,
X
is a union and all of its variant members are of const-qualified type (or array thereof),
X
is a non-union class and all members of any anonymous union member are of const-qualified type (or array thereof),
- any direct or virtual base class, or non-static data member with no brace-or-equal-initializer, has class type
Tôi
(or array thereof) and either Tôi
has no default constructor or overload resolution (13.3) as applied to Tôi
’s default constructor results in an ambiguity or in a function that is deleted or inaccessible from the defaulted default constructor, or
- any direct or virtual base class or non-static data member has a type with a destructor that is deleted or inaccessible from the defaulted default constructor.
A default constructor is trivial if it is not user-provided and if:
- its class has no virtual functions (10.3) and no virtual base classes (10.1), and
- no non-static data member of its class has a brace-or-equal-initializer, Và
- all the direct base classes of its class have trivial default constructors, and
- for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Otherwise, the default constructor is non-trivial.
由于 OP 中的结构 Điểm
有一个重要的默认构造函数,
Point() {}
包含 Điểm
类型成员的 union 的默认默认构造函数 应该根据第一个项目符号定义为删除:
X
is a union-like class that has a variant member with a non-trivial default constructor
导致 OP 中呈现的程序格式错误。
然而,根据 core working group issue 1623,如果 union 成员有 brace-or-equal-initializer,委员会似乎认为这是一个缺陷。 :
According to 12.1 [class.ctor] paragraph 5,
A defaulted default constructor for class X is defined as deleted if:
X
is a union-like class that has a variant member with a non-trivial default constructor,
...
X
is a union and all of its variant members are of const-qualified type (or array thereof),
X
is a non-union class and all members of any anonymous union member are of const-qualified type (or array thereof),
...
因为非静态数据成员初始化器的存在在道德上等同于 mem-initializer,所以应该修改这些规则,不要将生成的构造函数定义为当 union 成员具有一个非静态数据成员初始化器。 (注意 9.5 [class.union] 第 2-3 段和 7.1.6.1 [dcl.type.cv] 第 2 段中的非规范性引用,如果更改此限制,也需要更新。)
如果 union 的所有成员都具有 const 限定类型,则向 9.5 [class.union] 添加要求非静态数据成员初始化程序或用户提供的构造函数也会很有帮助。
在更一般的说明中,为什么仅仅因为成员具有非平凡的默认构造函数而将默认构造函数定义为已删除? union 本身不知道哪个成员是事件成员,并且默认构造不会初始化任何成员(假设没有 brace-or-equal-initializer)。由 union 的“所有者”来控制事件成员(如果有的话)的生命周期,并且需要用户提供的构造函数会强制使用没有意义的设计模式。同样,为什么仅仅因为一个成员有一个非平凡的析构函数就将默认析构函数定义为删除?如果它仅适用于 union 还具有用户提供的构造函数时,我会同意此限制。
Issue 1623 的状态为“drafting”,表明委员会认为该问题可能是一个缺陷 - 为什么要允许 union 成员使用 brace-or-equal-initializer? - 但尚未花时间确定决议的适当措辞。实际上,该段落在当前的 C++14 草案 N3936 ([class.ctor]/4) 中基本相同,只是“任何直接或虚拟基类或非静态数据成员”的措辞处处被替换为更简单的“任何可能构造的子对象”。
虽然两个编译器的行为并不严格符合,但我认为 Clang 的行为符合标准的精神。似乎 GCC 被删除的默认构造函数和 brace-or-equal-initializer 的组合弄糊涂了:
GCC 应该要么符合标准并将程序诊断为格式错误,要么模仿 clang 的行为并从 brace-or-equal-initializer 生成适当的构造函数。
Tôi là một lập trình viên xuất sắc, rất giỏi!