C++ 对于包含包装类型和类型本身的联合,是否有任何保证?

C++ 对于包含包装类型和类型本身的联合,是否有任何保证?,c++,struct,types,language-lawyer,unions,C++,Struct,Types,Language Lawyer,Unions,我能把一个T和一个包装好的T放在一个union中,并随意检查它们吗 union Example { T value; struct Wrapped { T wrapped; } wrapper; }; //为了简单起见,T=int 示例ex; ex.值=12; cout当访问不是上次写入的成员时,联合行为是未定义的。所以不,你不能依赖这种行为 这在原则上与使用并集从整数中提取特定字节的想法相同;但还有一个额外的风险,即您现在依赖于编译器,而不是在结构中添

我能把一个
T
和一个包装好的
T
放在一个
union
中,并随意检查它们吗

union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};
//为了简单起见,T=int
示例ex;
ex.值=12;

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


这在原则上与使用并集从整数中提取特定字节的想法相同;但还有一个额外的风险,即您现在依赖于编译器,而不是在结构中添加任何填充。有关更多详细信息,请参见。

它应该可以工作,因为
示例
包装
都是标准布局类,而C++14标准有足够的要求来保证在这种情况下
包装
位于同一地址。草案n4296在9.2类成员[Class.mem]§20中规定:

如果标准布局类对象具有任何非静态数据成员,则其地址与该地址相同 它的第一个非静态数据成员

一张便条甚至说:

[注意:因此,标准布局结构中可能存在未命名的填充 对象,但不是在其开始处,以实现适当对齐。-结束注释]

这意味着您至少遵守3.10左值和右值[basic.lval]§10中的严格别名规则

如果程序试图通过glvalue访问对象的存储值,而不是 以下类型的行为未定义
-对象的动态类型,

-在其元素或非静态元素中包含上述类型之一的聚合或联合类型 数据成员(递归地包括子集合的元素或非静态数据成员 或包含的联盟)

这是一个完美的定义:

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

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

给了我们:

两种标准布局结构类型的公共初始序列是按声明顺序排列的非静态数据成员和位字段的最长序列,从每个结构中的第一个这样的实体开始,这样,相应的实体具有布局兼容类型,并且两个实体都不是位字段,或者都是具有相同宽度的位字段。[……]

在具有结构类型为
T1
的活动成员的标准布局联合中,允许读取另一个结构类型为
T2
的联合成员的非静态数据成员
m
,前提是m是
T1
T2
的公共初始序列的一部分;该行为就像提名了
T1
的相应成员一样

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

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

因此,这里:

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


Example<int> ex;
ex.value = 12;
cout << ex.wrapper.wrapped; // (*)
模板
工会示例{
T值;
结构包装{
T包裹;
}包装纸;
};
示例ex;
ex.值=12;

你的第一段不适用于常见的初始序列。另外,我已经在我的问题中链接了Q&A;应该work@Zeta我很确定它能坚持下去。是的,您可能已经链接了这个问题,但是重新阅读它(注意,这里的答案同时包含C11和C++11),您将看到它声明在任何时候最多一个非静态数据成员的
值可以存储在一个联合中
-并且陷阱表示的概念已被删除。@UKMonkey不知道:/。这个问题的每个答案都至少有一张否决票(包括删除的那张)。呃,我不知道[defns.undefined]。谢谢你提出来。C+11的9.2§20中的措辞非常相似,但明确提到了指针:“指向标准布局结构对象的指针,使用重新解释转换进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向其所在的单元),反之亦然。”。因此,如果您想在答案中包含C++11,您可以引用n3337。取消引用具有正确地址和类型的指针,不会使其引用的对象可访问。@SergeBallesta Literaly:
reinterpret_cast(&union_a.member_a)->member_b
假设member_b是活动成员,成员a不需要处于活动状态。@奥利弗:这就是我使用指针的原因。由于刚刚分配了
,因此它是工会的活动成员。由于
value
wrapper.wrapped
具有相同的地址和类型,
&ex.wrapper.wrapped
实际上是指向有效成员的
ex.value
的指针。这就是它可以被取消引用的原因。@SergeBallesta,在标准中,从来没有说具有正确值和类型的指针是指向对象[basic.component]的指针,并且给出了许多示例,其中具有正确地址和类型的指针是无效指针。例如
intarr[10]{}*即使数组的地址与其第一个元素的地址相同,reinterpret_cast(&arr)=1
也是UB。
template <typename T>
union Example {
    T value;
    struct Wrapped { 
       T wrapped;
    } wrapper;
};


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