我试图更好地理解关于联合和共同的初始序列规则的相当令人惊讶的发现.常见的初始序列规则是(class.mem 23):
在具有struct类型T1的活动成员的标准布局并集中,允许读取结构类型T2的另一个union成员的非静态数据成员m,前提是m是T1和T2的公共初始序列的一部分; 行为就像提名T1的相应成员一样.
所以,给定:
struct A {
int a;
double x;
};
struct B {
int b;
};
union U {
A a;
B b;
};
U u;
u.a = A{};
int i = u.b.b;
Run Code Online (Sandbox Code Playgroud)
这是定义的行为,i应该具有值0(因为A并且B具有第一个成员的CIS,a int).到现在为止还挺好.令人困惑的是,如果B被简单的int替换:
union U {
A a;
int b;
};
...
int i = u.b;
Run Code Online (Sandbox Code Playgroud)
根据常见初始序列的定义:
两种标准布局结构类型的通用初始序列是......
所以CIS只能在两个标准布局结构之间应用.反过来:
标准布局结构是使用类键结构或类键类定义的标准布局类.
所以原始类型肯定不符合条件; 也就是说它没有任何CIS,所以A没有CIS int.因此标准说第一个例子是定义的行为,但第二个例子是UB.这对我来说根本没有任何意义; 直觉上,编译器至少受原始类型和类的限制.如果这是故意的,是否有任何押韵或理由(可能是对齐相关的)为什么这是有意义的?这可能是一个缺陷吗?
我可以把T和一个包裹T在union并检查他们,因为我喜欢?
union Example {
T value;
struct Wrapped {
T wrapped;
} wrapper;
};
Run Code Online (Sandbox Code Playgroud)
// for simplicity T = int
Example ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // ?
Run Code Online (Sandbox Code Playgroud)
C++ 11标准只保证保存对常见初始序列的检查,但value不是struct.我猜答案是否定的,因为包装类型甚至不能保证与其解包对应的内存兼容,并且访问非活动成员只能在常见的初始序列上明确定义.