C++11 中 std::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;
}

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

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

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

解决方法

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是任意的。有些情况是允许的,但范围很窄。

,

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

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

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

  2. 对于使用特定于编译器的语法扩展语言以支持类型双关的实现,请使用它。

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

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