包含包装类型和类型本身的联合是否有任何保证?

Are there any guarantees for unions that contain a wrapped type and the type itself?

我可以把一个 T 和一个包裹的 T 放在一个 union 中并随意检查它们吗?

union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};
// for simplicity T = int

Example ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // ?

C++11 标准只保证公共初始序列的保存检查,但 value 不是 struct。我猜测答案是,因为 and accessing inactive members is only well-defined on common initial sequences.

当访问一个不是最后写入的成员时,联合行为是未定义的。所以不,你不能依赖这种行为。

它在原理上与使用联合从整数中提取特定字节的想法相同;但有一个额外的风险,即您现在依赖编译器不在您的结构中添加任何填充。有关详细信息,请参阅 Accessing inactive union member and undefined behavior?

它应该可以工作,因为 ExampleWrapped 都是 标准布局 类,并且 C++14 标准有足够的要求保证在这种情况下 valuewrapper.wrapped 位于同一地址。 n4296 草案在 9.2 Class 成员 [class.mem] §20:

中说

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member.

一张纸条甚至说:

[ Note: There might therefore be unnamed padding within a standard-layout struct object, but not at its beginning, as necessary to achieve appropriate alignment. —end note ]

这意味着您至少要遵守 3.10 左值和右值 [basic.lval] §10

中的 严格别名规则

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined
— the dynamic type of the object,
...
— an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),

所以这是完美的定义:

cout << *(&ex.wrapper.wrapped) << endl

因为 &ex.wrapper.wrapped 需要与 &ex.value 相同,并且指向的对象具有正确的类型。 . 但由于标准仅对 公共子序列 是显式的。所以我的理解是 cout << ex.wrapper.wrapped << endl 调用未定义的行为,因为 1.3.24 [defns.undefined] 中的注释 未定义的行为 说(强调我的):

Undefined behavior may be expected when this International Standard omits any explicit definition of behavior...

TL/DR:我敢打赌,即使不是所有常见实现也会接受它,但由于 1.3.24 [defns.undefined] 的注释,我永远不会在生产代码,但会使用 *(&ex.wrapper.wrapped) 代替。


在 C++17 的最新草案 n4659 中,相关概念是 相互转换([basic.compound] §4)。

我认为这是未定义的行为。

[class.mem] 给我们:

The common initial sequence of two standard-layout struct types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types and either neither entity is a bit-field or both are bit-fields with the same width. [...]

In a standard-layout union with an active member of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2; the behavior is as if the corresponding member of T1 were nominated.

如果 T 不是标准布局结构类型,这显然是未定义的行为。 (请注意 int 不是标准布局结构类型,因为它根本不是 class 类型)。

但即使对于标准布局结构类型,构成“公共初始序列”的内容也严格基于非静态数据成员。也就是说,Tstruct { T val; } 没有共同的初始序列 - 根本没有共同的数据成员!

因此,这里:

template <typename T>
union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};


Example<int> ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // (*)

您正在访问一个不活跃的工会会员。那是未定义的。