この記事のポイント

  • swap活用によるリソース制御責任をレビューアー視点で整理
  • 例外安全、破壊最小化、ムーブ安全化の設計技術を実戦的に解説
  • API契約・ライフサイクル責任まで含めたレビュー観点を体系化

そもそもswapとは

C++標準ライブラリのswapは

  • 二つのオブジェクトの中身を効率的に交換
  • 通常はムーブ操作で構成
std::swap(a, b);

動作概念

  • 所有リソースのポインタ・内部状態を交換
  • コピー負荷が無い(ムーブ可能なら高速)
  • swapは代入の代替手段ではなく破壊最小化戦略
  • 例外安全設計の武器

なぜこれをレビューするのか

レビューアー視点

swap適用設計には以下の責任整理が必要。

  • 破壊的代入リスク排除責任
    → コピー代入中の例外発生を未然排除

  • 一時リソース確保削減責任
    → move/swap適用によるメモリ確保回数削減確認

  • ライフサイクル責任整理
    → swap後の寿命責任明確化

  • API契約責任整理
    → swap可否を契約文書化(コピー禁止型設計)

  • 自己代入安全化責任
    → swapは自己代入でも安全

開発者視点

  • 代入演算子実装時swap利用を忘れる
  • 例外安全意識不在
  • コピー代入危険性軽視
  • ムーブセマンティクス文化不足
  • swapを「ただの便利関数」と誤認

レビューアーはこれら「安全設計軽視文化」を初期段階で是正する役割を担う。

良い実装例:代入演算子の例外安全化

ユースケース:独自バッファ管理クラス

良い実装例:copy-swapイディオム
class Buffer {
public:
    Buffer(size_t size) : size_(size), data_(new char[size]) {}

    ~Buffer() { delete[] data_; }

    Buffer(const Buffer& other)
        : size_(other.size_), data_(new char[other.size_]) {
        std::copy(other.data_, other.data_ + other.size_, data_);
    }

    Buffer& operator=(Buffer other) noexcept {
        swap(*this, other);
        return *this;
    }

    friend void swap(Buffer& a, Buffer& b) noexcept {
        using std::swap;
        swap(a.size_, b.size_);
        swap(a.data_, b.data_);
    }

private:
    size_t size_;
    char* data_;
};
  • 代入演算子でcopy-swapイディオム適用
  • 自己代入安全化
  • 例外安全保証(noexcept化容易)

レビュー観点

  • copy-swap適用による例外安全化が整理されているか
  • swap実装が自クラス用カスタムswap提供になっているか
  • ライフサイクル責任がムーブセマンティクス準拠で整理されているか
  • API契約文書にムーブ優先・swap安全化設計が整理されているか

良くない実装例: ケース1

以下はcopy代入中に部分的に例外発生する危険な設計例。

危険なcopy代入例
Buffer& operator=(const Buffer& other) {
    if (this != &other) {
        delete[] data_;
        size_ = other.size_;
        data_ = new char[size_];
        std::copy(other.data_, other.data_ + size_, data_);
    }
    return *this;
}
@Reviewer
例外発生時に部分破壊状態が残存します。copy-swap適用で例外安全化を設計してください。

問題点

  • new失敗時にオブジェクト不整合
  • 例外安全性破壊

改善例

修正例:copy-swap適用
Buffer& operator=(Buffer other) noexcept {
    swap(*this, other);
    return *this;
}

良くない実装例: ケース2

次はswap実装を誤って標準swap呼出に依存してしまう例。

標準swap誤依存例
void swap(Buffer& a, Buffer& b) noexcept {
    std::swap(a, b); // 無限再帰
}
@Reviewer
swap内部ではメンバ単位でswap実行し、標準swap循環呼出を防止してください。

問題点

  • swap実装ループ誘発
  • メンバ単位swap責任不在

改善例

修正例:メンバ単位swap
using std::swap;
swap(a.size_, b.size_);
swap(a.data_, b.data_);

良くない実装例: ケース3

次は自己代入判定分岐が過剰防御化している例。

自己代入判定過剰例
if (this != &other) {
    // copy代入処理
}
@Reviewer
copy-swap設計なら自己代入安全です。分岐除去可能です。

問題点

  • 過剰条件判定
  • 設計責任整理不足

改善例

修正例:分岐削除
Buffer& operator=(Buffer other) noexcept {
    swap(*this, other);
    return *this;
}
  • copy-swapは自己代入安全

swap適用場面整理

用途 swap活用箇所
copy代入安全化 copy-swapイディオム
例外安全化 リソース寿命制御
破壊縮小 std::vector swapによる領域開放
状態交換 algorithm内部

swap適用における設計責任

パターンA:独自swap提供(ADL優先)

friend void swap(MyType& a, MyType& b) noexcept;
  • メンバ単位swap実装
  • 標準swap循環回避

パターンB:ライブラリswap準拠

using std::swap;
swap(memberA, memberB);
  • 標準コンテナを安全にswap適用可能

swapとムーブの責任整理比較

項目 swap move
両者対象 2オブジェクト 1オブジェクト
メモリ確保回数 0 1
自己代入安全性 完全 不要
例外安全性 強い ムーブ次第
破壊最小化性 非常に高い 高い

PlantUMLで設計責任整理

UML Diagram

観点チェックリスト

実務レビューFAQ

Q1. swapは必ず提供する?
→ ユーザ定義型では提供検討。特にリソース管理型で有効。

Q2. swapは自己代入時も安全?
→ 完全安全。保険条件分岐不要。

Q3. moveがあればswap不要?
→ 役割が異なる。代入安全化・破壊最小化用途に残る。

Q4. 標準swapに委ねて良い?
→ カスタム型ではメンバ単位swap必須。循環回避設計が重要。

Q5. swapは例外安全になる?
→ 強例外保証を簡易実現可能。レビュー時は積極設計対象。

まとめ

swap活用レビューは例外安全設計とリソース制御責任の訓練教材である。
レビューアーは

  • copy-swap適用責任
  • ムーブ責任整理
  • 自己代入安全性検証
  • API契約昇格

を読み解き、「破壊を避ける文化設計」を支援するレビュー技術を育成する必要がある。
レビューアーがswap設計責任を正しく整理できると例外安全設計品質は大きく向上する。