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

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

本文关键字:类型 小类 之间 转换 cast reinterpret 为什么 copy      更新时间:2023-10-16

根据cppreference.com,reinterpret_cast:

通过重新解释基础位模式在类型之间进行转换。

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

当指向类型T1的对象的指针或引用是指向不同类型T2的对象的指示器或引用的reinterpret_cast(或C样式转换)时,转换总是成功的,但是,只有当T1T2都是标准布局类型并且以下情况之一为真时,才能访问结果指针或引用:

  • T2是对象的(可能是cv限定的)动态类型
  • T2T1都是指向同一类型T3的指针(可能是多级的,可能是在每个级别限定的cv)
  • T2是对象的动态类型的有符号或无符号变体(可能是cv限定的)
  • T2是一个聚合类型或并集类型,它将上述类型之一作为元素或非静态成员(递归地包括子聚合的元素和所包含并集的非静态数据成员):这使得从结构的第一个成员和从并集的一个元素强制转换到包含它的结构/并集是安全的
  • T2是对象的动态类型的基类(可能是cv限定的)
  • T2charunsigned 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不帮我处理呢?

一个原因是没有指定大小、对齐方式和位表示,所以这样的转换是不可移植的。然而,这并不能真正证明定义行为,而只是定义实现。

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

int   & i = something();
float & f = something_else();
const int i1 = i;
f = 42;
const int i2 = i;

编译器可以假设i1i2都具有相同的值(i通过对f的赋值而保持不变)并将它们优化为单个常数。打破这种假设会导致不明确的行为。

或者还有其他东西可以让我不必跳过这个环吗?

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

reinterpret_cast或并集别名有时可能会起作用(假设大小等匹配),但如果优化器对未定义的行为过于聪明,则可能会让您绊倒。

主要是由于对reinterpret_cast的限制(cpprreference站点未完全捕获)

  • 对齐问题和
  • 陷阱表示

对于一个假设的reinterpret_cast到数值,可以通过截断或零扩展轻松处理不同的大小,因为无论如何都会进入危险的比特级区域,所以这不是问题。

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


请注意,标准对任何东西的保证都可以并且通常由任何特定的编译器扩展。

通常情况下,以比仅依赖标准的便携性低一点为目标是一个好主意。

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