Aar*_*aid 26 c++ object-lifetime unions language-lawyer c++11
重要的澄清:一些评论者似乎认为我是从工会复制的.仔细查看memcpy,它从一个普通旧的地址复制uint32_t,它不包含在一个联合中.此外,我正在(通过memcpy)复制到一个联盟的特定成员(u.a16或者&u.x_in_a_union,不是直接复制到整个联盟本身(&u)
C++对工会非常严格 - 只有在成员写入的最后一个成员时才应该从成员中读取:
9.5 Unions [class.union] [[c ++ 11]]在一个联合中,最多一个非静态数据成员可以随时处于活动状态,即最多一个非静态的值数据成员可以随时存储在联合中.
(当然,编译器不会跟踪哪个成员是活动的.由开发人员来确保他们自己跟踪它)
更新:以下代码块是主要问题,直接反映问题标题中的文本.如果这段代码没问题,我会对其他类型进行跟进,但我现在意识到第一块代码本身很有趣.
#include <cstdint>
uint32_t x = 0x12345678;
union {
double whatever;
uint32_t x_in_a_union; // same type as x
} u;
u.whatever = 3.14;
u.x_in_a_union = x; // surely this is OK, despite involving the inactive member?
std::cout << u.x_in_a_union;
u.whatever = 3.14; // make the double 'active' again
memcpy(&u.x_in_a_union, &x); // same types, so should be OK?
std::cout << u.x_in_a_union; // OK here? What's the active member?
Run Code Online (Sandbox Code Playgroud)
紧接在此之上的代码块可能是评论和答案中的主要问题.事后看来,我不需要在这个问题中混合类型!基本上,是u.a = b一样memcpy(&u.a,&b, sizeof(b)),假设类型是相同的?
首先,一个相对简单的memcpy允许我们读取一个uint32_t数组uint16_t:
#include <cstdint> # to ensure we have standard versions of these two types
uint32_t x = 0x12345678;
uint16_t a16[2];
static_assert(sizeof(x) == sizeof(a16), "");
std:: memcpy(a16, &x, sizeof(x));
Run Code Online (Sandbox Code Playgroud)
准确的行为取决于平台的字节顺序,您必须注意陷阱表示等.但是,我们普遍同意这一点(我认为?反馈意见!),在谨慎避免有问题的价值观的情况下,上述代码可以在正确的平台上在正确的背景下完美地进行标准投诉.
(如果您对上述代码有疑问,请相应地评论或编辑问题.在继续下面的"有趣"代码之前,我想确定我们有一个没有争议的上述版本.)
如果且仅当两个代码块都不是-UB时,那么我想将它们组合如下:
uint32_t x = 0x12345678;
union {
double whatever;
uint16_t a16[2];
} u;
u.whatever = 3.14; // sets the 'active' member
static_assert(sizeof(u.a16) == sizeof(x)); //any other checks I should do?
std:: memcpy(u.a16, &x, sizeof(x));
// what is the 'active member' of u now, after the memcpy?
cout << u.a16[0] << ' ' << u.a16[1] << endl; // i.e. is this OK?
Run Code Online (Sandbox Code Playgroud)
工会的哪个成员,u.whatever或者u.a16是"活跃成员"?
最后,我自己的猜测是,在实践中,我们关心这一点的原因是优化编译器可能没有注意到memcpy发生的事情,因此做出错误的假设(但是通过标准允许的假设)关于哪个成员是活动的和哪些数据类型是"活动的",因此会导致别名错误.编译器可能会memcpy以奇怪的方式重新排序.这是对我们关心的原因的恰当总结吗?
我对该标准的解读是,std::memcpy只要该类型可以轻易复制,就是安全的.
从9个类中,我们可以看到unions是类类型,因此可以轻易地复制它们.
甲联盟是与所定义的一类类键结合; 它一次只保存一个数据成员(9.5).
一个简单的可复制类是一个类:
- 没有非平凡的复制构造函数(12.8),
- 没有非平凡的移动构造函数(12.8),
- 没有非平凡的副本赋值运算符(13.5.3,12.8),
- 没有非平凡的移动赋值运算符(13.5.3,12.8),和
- 有一个简单的析构函数(12.4).
3.9类型中给出了平滑可复制的确切含义:
对于具有普通可复制类型的任何对象(基类子对象除外)
T,无论对象是否保持有效的类型值,构成对象T的基础字节(1.7)都可以复制到char或的数组中unsigned char.如果阵列的内容char或unsigned char将被复制回对象,该对象随后应保持其原始值.对于任何平凡复制的类型
T,如果两个指针T指向不同T对象obj1和obj2,其中既不obj1也不obj2是碱基-类子对象,如果构成底层字节(1.7)obj1被复制到obj2,obj2随后应保持相同的值obj1.
该标准还给出了两者的明确示例.
因此,如果您复制整个联合,答案肯定是肯定的,活动成员将与数据一起"复制".(这是相关的,因为它表明std::memcpy 必须将其视为更改联合的活动元素的有效方法,因为明确允许使用它来进行整个联合复制.)
现在,您正在复制到联盟的成员.该标准似乎不需要任何特定的分配给工会成员的方法(因此使其处于活动状态).它所做的只是指定(9.5)
[注意:通常,必须使用显式析构函数类和放置新运算符来更改联合的活动成员. - 结束说明]
当然,它说,因为C++ 11允许联合中非平凡类型的对象.注意前面的"一般",这清楚地表明在特定情况下允许更换活动成员的其他方法; 我们已经知道这是因为明确允许转让.当然,没有禁止使用std::memcpy,否则其使用将是有效的.
所以我的答案是肯定的,这是安全的,是的,它改变了活跃的成员.
| 归档时间: |
|
| 查看次数: |
1373 次 |
| 最近记录: |