C++11 中 std::bit_cast 的安全等价物

Safe equivalent of std::bit_cast in C++11

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;
}

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

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

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

几乎每个 C 或 C++ 编译器,可能包括每个适用于涉及 bit_cast 的任务的编译器,都包含配置选项以通过定义标准没有强加要求的行为来扩展语言。我还没有看到任何已发布的 C++ 标准基本原理文档,但 C 标准的作者明确地将 UB 描述为识别“符合语言扩展”的区域,并将对此类“流行扩展”的支持视为quality-of-implementation 标准管辖范围之外的问题。

构造不能在所有 C 或 C++ 编译器配置中移植的事实并不意味着它不能或不应该安全地用于支持它的配置。我建议设计一些 headers 可以定义宏来处理事情的三种方式之一,具体取决于实现方式:

  1. 对于支持 bit_cast 的实现,请使用它。

  2. 对于使用 compiler-specific 语法扩展语言以支持类型双关的实现,请使用它。

  3. 对于其他实现,指定编译器必须配置为扩展语言,至少在使用常见操作序列时支持类型双关。

虽然可能存在 none 这些方法适用的实现,但在大多数实现中,至少其中一种方法是有效的。

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;
}

您或许可以将 pod 限制放宽到可简单复制。

编译器非常好,上面针对良好的汇编进行了优化。

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