在以下程序中,
union U
具有类型的活性成员,该成员是使用自动生成的A
复制到类型operator =
的另一个成员中的,将B
作为subobject:A
我想获得#include <iostream>
struct A { char c[7]; };
struct X { char x; };
struct B : X, A {
using A::operator=;
};
int main() {
union U {
A a{ 0, 1, 2, 3, 4, 5, 6 };
B b;
} u;
//#1
u.b = u.a;
//#2
//u.b = A( u.a );
for ( int i = 0; i < 7; ++i )
std::cout << (int)u.b.c[i];
}
输出,但是没有优化的实际编译器会产生不同的输出。
MSVC:
0123456
0000000
0123355
问题1:是因为该程序在复制过程中同时访问了联盟的活动和不活动成员,因此该程序具有不确定的行为?如果不是
0122356
u.b = u.a;
u.b = A( u.a );
,但是GCC和EDG坚持使用相同的
0123456
。在线演示:https://gcc.godbolt.org/z/dxfzem5em
问题2:该程序现在是否形成良好,意外结果是由于实现中的错误造成的吗?在一个给定时刻([class.union.general]/2
)的联合中,最多有一个成员可以活跃(寿命内)。
升级u.b = u.a
成员的寿命,从而结束了b
[basic.life]/2.5
)的寿命。根据[class.union.general]/5,
...(对于创建对象)没有执行初始化,并且其生命周期的开始是在左右操作数的值计算之后和分配之前对其进行测序的。在分配发生的时间,
u.a
不在一生之内,因此从中读取的行为不确定。
在对比中,在tempor临时对象的初始化中发生在u.a
仍处于活动状态时,避免了生命周期的问题并使分配明确定义。
worth指出,上述措辞首先仅出现在C ++ 17中(起源于示例似乎表明,通过分配的主动联盟成员的隐式变化至少也旨在在当时有效。P0137
)。在此之前,工会与对象寿命的相互作用在很大程度上未指定,因此基于C ++ 11的规范文本,可能没有确定的答案。但是,[class.union]中的一个