Skip to content

bit_cast 実装イメージと未定義動作の話 #664

@k-satoda

Description

@k-satoda

@faithandbrave さんの commit 1060be9bit_cast.md を見て気になったことです。
概要より:

低レイヤーのプログラムでは、同じビットを維持してほかの型に解釈し直すことが必要とされる。その際、reinterpret_castや共用体を使用すると、Strict Aliasing規則に抵触してしまい未定義動作になってしまう。そのような目的にはstd::aligned_storagestd::memcpy()を組み合わせて使用することになるが、それを簡単に使用できるようにしたのが本関数である。

備考にある実装イメージより:

template<typename To, typename From>
To bit_cast(const From& from) noexcept { // 実際には、さらに要件チェックが行われる
  // To型のオブジェクトに直接memcpyするのではなく、アライメント調整した領域にmemcpyして、
  // その領域をTo型に再解釈キャストする。
  typename std::aligned_storage<sizeof(To), alignof(To)>::type storage;
  std::memcpy(&storage, &from, sizeof(To));  // memcpyはconstexprではないため、
                                             // コンパイラが特殊な実装をする必要がある
  return reinterpret_cast<To&>(storage);
}

僕の知る限りこの return reinterpret_cast<T&>(storage)return reinterpret_cast<To&>(from) と同じく
実際のオブジェクトと無関係な型でのアクセスを起こすという理由で未定義動作の範囲内にあり、
違うのは memcpy を挟むことで現代のコンパイラで実際に誤動作に至る可能性をほぼ無くせること、
と認識しています。(この効果を一定条件下で規定しちゃおうというのが https://wg21.link/P0593 の目的、と。)

(↑の認識が正しいとして・・・)
現状の記事のままだと、概要の文面と合わせてこの aligned_storage と memcpy を使ったコードが
未定義動作を回避するものと読み取られそうでヤダなーと思ったんですが、だからといって
既存の手法や実装方法に関する記述をざっくり削除するというのも望ましくないものと思います。
というわけで、いったん issue にしてみることにしました。
認識の正誤や記事の改善方針について意見もらえるとうれしいです。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions