C++11 中 std::bit_cast 的安全等效项

Cra*_*ney 4 c++ casting undefined-behavior c++11 bit-cast

C++20 引入了std::bit_cast来处理相同的位,就好像它们是不同的类型一样。所以,基本上它是这样做的:

template <typename T1, typename T2>
T2 undefined_bit_cast(T1 v1) {
    T1 *p1 = &v1;
    T2 *p2 = (T2 *)p1; // Uh oh.
    T2 v2 = *p2; // Oh no! Don't do that!
    return v2;
}
Run Code Online (Sandbox Code Playgroud)

除非没有未定义的行为,允许编译器用硬盘删除方法替换此方法。

我想使用 std::bit_cast,但我坚持使用 C++11。如何正确实现我自己的custom_bit_cast,不使用未定义的行为,而不使用实际的std::bit_cast

后续问题:是否有指针位转换?有一种安全的方法可以让 aT1 *和 aT2 *指向同一位置,并且能够从两者读取和写入,而不会出现未定义的行为?

Yak*_*ont 6

template <class T2, class T1>
T2 cpp11_bit_cast(T1 t1) {
  static_assert(sizeof(T1)==sizeof(T2), "Types must match sizes");
  static_assert(std::is_pod<T1>::value, "Requires POD input");
  static_assert(std::is_pod<T2>::value, "Requires POD output");

  T2 t2;
  std::memcpy( std::addressof(t2), std::addressof(t1), sizeof(T1) );
  return t2;
}
Run Code Online (Sandbox Code Playgroud)

您可以将 pod 限制放宽到可轻松复制。

编译器非常好,上面的内容被优化为良好的汇编。

至于指针位,没有安全的方法来读取类型的对象,T1就好像它是类型的对象,T2其中T1T2是任意的。有些情况是允许的,但范围很窄。

  • 编译器优化“memcpy”的能力取决于平台。在 32 位 ARM 平台上,配置为“-fno-strict-aliasing”或等效项的编译器可能能够使用以下类型执行“*(T1*)p1 = *(T2*)p2;”:每个使用加载多个指令和存储多个指令来保存四个字,但是使用“memcpy”将需要使用 16 个加载和 16 个存储,或者调用一个检查对象对齐并使用 32 位的函数尽可能加载/存储,但在需要时使用字节加载/存储。 (2认同)