11

您能否提出至少一种情况存在重大差异的情况?

union {
T var_1;
U var_2;
}

var_2 = reinterpret_cast<U> (var_1)

?

我对此想得越多,至少从实际的角度来看,它们对我来说就越像同一件事。

我发现的一个区别是,虽然联合大小在大小方面是最大的数据类型,但本文中描述的 reinterpret_cast 可能会导致截断,因此普通的旧 C 风格联合比新的更安全C++ 铸造。

你能概述一下这 2 之间的区别吗?

4

4 回答 4

8

与其他答案所述相反,从实际角度来看,存在巨大差异,尽管标准可能没有这样的差异。

从标准的角度来看,reinterpret_cast只有当中间指针类型的对齐要求不强于源类型的对齐要求时,才保证适用于往返转换。不允许(*)读取一个指针并读取另一种指针类型。

同时,该标准要求联合的类似行为,读取除活动成员(最后写入的成员)以外的联合成员(+)是未定义的行为。

然而,编译器通常为联合情况提供额外的保证,我所知道的所有编译器(VS、g++、clang++、xlC_r、intel、Solaris CC)都保证您可以通过非活动成员读取联合,并且它会产生一个值与通过活动成员写入的位设置完全相同。

这对于从网络读取时的高度优化尤为重要:

double ntohdouble(const char *buffer) {          // [1]
   union {
      int64_t   i;
      double    f;
   } data;
   memcpy(&data.i, buffer, sizeof(int64_t));
   data.i = ntohll(data.i);
   return data.f;
}
double ntohdouble(const char *buffer) {          // [2]
   int64_t data;
   double  dbl;
   memcpy(&data, buffer, sizeof(int64_t));
   data = ntohll(data);
   dbl = *reinterpret_cast<double*>(&data);
   return dbl;
}

[1] 中的实现得到了我所知道的所有编译器(gcc、clang、VS、sun、ibm、hp)的认可,而 [2] 中的实现则没有,并且当使用激进的优化时,其中一些编译器会严重失败。特别是,我看到 gcc在评估ntohl之前重新排序指令并入变量,从而产生错误的结果。dbl


(*)除了你总是被允许从 a中读取[signed|unsigned] char*,而不管真实对象(原始指针类型)是什么。

(+)同样,除了一些例外,如果活动成员与另一个成员共享一个公共前缀,您可以通读该前缀的兼容成员。

于 2013-07-29T13:24:29.110 回答
5

正确和安全之间存在一些技术差异union(让我们假设)正确和安全reinterpret_cast。但是,我想不出任何无法克服的差异。

在我看来,更喜欢 a的真正原因不是技术原因。它用于文档。unionreinterpret_cast

假设您正在设计一堆类来表示一个有线协议(我猜这是首先使用类型双关语的最常见原因),并且该有线协议由许多消息、子消息和字段组成。如果其中一些字段很常见,例如 msg 类型、seq# 等,则使用联合可以简化将这些元素绑定在一起的过程,并有助于准确记录协议在网络上的显示方式。

Usingreinterpret_cast显然做同样的事情,但为了真正了解发生了什么,您必须检查从一个数据包前进到下一个数据包的代码。使用 aunion您只需查看标题并了解发生了什么。

于 2013-07-29T12:39:38.043 回答
1

在 C++11 中, union 是类类型,你可以持有一个具有非平凡成员函数的成员。你不能简单地从一个成员转换到另一个成员。

§ 9.5.3

[ 示例:考虑以下联合:

union U {
int i;
float f;
std::string s;
};

由于 std::string (21.3) 声明了所有特殊成员函数的非平凡版本,U 将具有隐式删除的默认构造函数、复制/移动构造函数、复制/移动赋值运算符和析构函数。要使用 U,这些成员函数中的部分或全部必须由用户提供。—结束示例]

于 2013-07-29T12:23:13.033 回答
-1

从实际的角度来看,它们很可能是 100% 相同的,至少在真实的非虚构计算机上是这样。您采用一种类型的二进制表示并将其填充到另一种类型中。

从语言律师的角度来看,reinterpret_cast在某些情况下使用是明确定义的(例如指向整数转换的指针),而在其他情况下是特定于实现的。

另一方面,联合类型双关语总是非常明显的未定义行为(尽管未定义不一定意味着“不起作用”)。该标准规定,最多一个非静态数据成员的值可以随时存储在联合中。这意味着如果你设置var1thenvar1是有效的,但var2不是。
但是,由于var1var2存储在相同的内存位置,您当然仍然可以根据需要读取和写入任何类型,并且假设它们具有相同的存储大小,不会“丢失”任何位。

于 2013-07-29T12:36:39.433 回答