为什么 reinterpret_cast 不强制 copy_n 进行相同大小类型之间的转换?

Why Doesn't reinterpret_cast Force copy_n for Casts between Same-Sized Types?

根据cppreference.comreinterpret_cast

Converts between types by reinterpreting the underlying bit pattern.

但是等等,这是个谎言,因为它只在这些情况下有效:

When a pointer or reference to object of type T1 is reinterpret_cast (or C-style cast) to a pointer or reference to object of a different type T2, the cast always succeeds, but the resulting pointer or reference may only be accessed if both T1 and T2 are standard-layout types and one of the following is true:

  • T2 is the (possibly cv-qualified) dynamic type of the object
  • T2 and T1 are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T3
  • T2 is the (possibly cv-qualified) signed or unsigned variant of the dynamic type of the object
  • T2 is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to cast from the first member of a struct and from an element of a union to the struct/union that contains it.
  • T2 is a (possibly cv-qualified) base class of the dynamic type of the object
  • T2 is char or unsigned char

根据该列表,非法示例为:

auto foo = 13LL;
auto bar = reinterpret_cast<double&>(foo);

所以唯一可接受的转换方式是复制内存:

auto foo = 13LL;
double bar;

copy_n(reinterpret_cast<char*>(&foo), sizeof(foo), reinterpret_cast<char*>(&bar));

我的问题是,为什么 reinterpret_cast 不为我处理?还是有其他可用的东西,这样我就不必跳过这个圈套了?

主要是reinterpret_cast的限制(没有完全被cppreference网站捕获)是因为

  • 对齐问题和
  • 陷阱表示。

对于假设的 reinterpret_cast 到数值,不同的大小可以很容易地通过截断或零扩展来处理,因为无论如何都会进入危险的位级区域,所以这不是问题。

通过使用 memcpycopy_n 您可以解决对齐问题,但您仍然可能成为陷阱表示的受害者。这意味着使用结果值可能会崩溃。在某些平台上,对于某些值。


请注意,标准对任何事情的保证,可以而且通常由任何特定的编译器扩展。

通常情况下,将可移植性比仅仅依靠标准来获得的可移植性稍微差一点是个好主意。

例如,当您不能假设一个字节是 8 位时,事情就会很快变得复杂。做出这种假设会降低可移植性。但支持的平台集仍然很大。

why doesn't reinterpret_cast handle that for me?

一个原因是未指定大小、对齐方式和位表示,因此此类转换不可移植。但是,这并不能真正证明使行为未定义,只是实现定义的行为是合理的。

通过将其设为未定义,允许编译器假定不相关类型的表达式不会访问同一对象,这样可以实现更好的优化。例如,在下面:

int   & i = something();
float & f = something_else();

const int i1 = i;
f = 42;
const int i2 = i;

编译器可以假定 i1i2 都具有相同的值(i 未被赋值给 f),并将它们优化为单一常数。打破假设将导致未定义的行为。

Or is there something else available so I don't have to jump through this hoop?

复制字节是将一种对象类型重新解释为不相关类型的唯一定义明确的方法。

使用 reinterpret_cast 或联合的别名有时可能会起作用(假设大小等匹配),但如果优化器对未定义的行为过于聪明,则可能会误导你。