静的アサーション



カテゴリ static_assert( … ) マクロ 保証内容 アサートする場面・理由 典型的な失敗シナリオ
生存期間 / 破棄 std::is_nothrow_destructible_v<T> デストラクタが noexcept として宣言されている → 例外を送出できない。 オブジェクトはスタックの巻き戻し中やワーカースレッド上で破棄される可能性があり、例外を送出するデストラクタは std::terminate を呼び出す。 誰かが ~T() { if(err) throw; } を追加する、またはデストラクタが例外を送出し得るメンバを追加する。
  std::is_trivially_destructible_v<T> コンパイラがデストラクタで何もしない(ビット単位での破棄)。 極めて低レベルな POD / ロックフリーコードでのみ必要(例: std::atomic<T> に格納する場合)。 std::vector、スマートポインタ、またはユーザー定義のデストラクタを追加する。
ムーブ std::is_nothrow_move_constructible_v<T> T のムーブが例外を送出しない。 コンテナ(std::vector、std::optional)が強い例外安全性を保ち、コピーを回避できることを保証する。 ムーブコンストラクタから noexcept を削除する、またはムーブ時に例外を送出するメンバを追加する。
  std::is_nothrow_move_assignable_v<T> ムーブ代入が noexcept である。 上記と同じ理由。加えて、高速な swap に依存するアルゴリズムを高速化する。 前の列と同じ。
コピー防止 !std::is_copy_constructible_v<T> コピー構築が削除されている。 大きなオブジェクトや一意であるべきオブジェクトは、誤ってコピーされることが決してあってはならない(例: 変換テーブル)。 開発者が = delete を削除し、意図せず高コストなコピーを有効化してしまう。
  !std::is_copy_assignable_v<T> コピー代入が削除されている。 上記と同じ。 同上。
コピーコスト / POD 性 std::is_trivially_copyable_v<T> オブジェクトを生の memcpy で移動できる(独自のコピー/ムーブ/デストラクタがない)。 高頻度で値渡しされる小さな値型、ロックフリーキュー内のデータ。 スマートポインタ、またはユーザー定義のコピー/ムーブ/デストラクタを追加する。
スワップ可能性 (C++17) std::is_nothrow_swappable_v<T> swap(a,b) が noexcept かつ低コストである。 STL アルゴリズムが高速なスワップ処理を使えるようにする(例: std::sort、std::map::insert の内部)。 独自の swap() が noexcept を失う、またはムーブ操作が例外を送出するようになる。


適用方法

#include <type_traits>
#include <utility>          // for std::is_nothrow_swappable_v

static_assert(std::is_nothrow_move_constructible_v<GTranslationTable>,
              "GTranslationTable move-ctor must be noexcept for vector reallocation!");

static_assert(!std::is_copy_constructible_v<GTranslationTable>,
              "Copying a translation table is disallowed — use move or shared_ptr.");


適切なアサートの選び方

意図 推奨する型特性
パフォーマンスが重要な値型 std::is_trivially_copyable_v<T>
リソースを保持し、ムーブのみを行う型 コピー操作を削除し、例外を送出しないムーブに関する 2 つの型特性をアサートする
スレッド間で共有されるオブジェクト 少なくとも std::is_nothrow_destructible_v<T>
独自の swap / 順序付きマップで使用 std::is_nothrow_swappable_v<T> を追加する