この記事のポイント

  • コピー・ムーブ構築/代入4種の意図を整理
  • レビューで意図不一致や自動生成暴走をどう検出するか解説
  • 実務コードの改善例・レビュー観点を提示

C++特殊メンバ 4種の構造整理

C++では以下の特殊メンバが存在する。

種別 シグネチャ 役割
コピーコンストラクタ ClassName(const ClassName&) 新規インスタンスへコピー初期化
ムーブコンストラクタ ClassName(ClassName&&) 新規インスタンスへ所有権移譲初期化
コピー代入演算子 ClassName& operator=(const ClassName&) 既存インスタンスへコピー代入
ムーブ代入演算子 ClassName& operator=(ClassName&&) 既存インスタンスへ所有権移譲代入

:::info

ここがレビュー本質

この4種は「資源責務の転送ルール」そのものである

:::


自動生成任せの危険性

① 資源所有権が二重管理に陥る

FileHandle a(3);
FileHandle b = a;  // fd 3 が重複保持

② ムーブできない型にムーブ想定代入が暴走

③ 保守拡張時に不整合設計が露呈

  • 成員追加で自動生成が期待とずれる

C++11以降の正しい設計表現:明示定義

class FileHandle {
public:
    explicit FileHandle(int fd) : fd_(fd) {}

    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;

    FileHandle(FileHandle&&) noexcept = default;
    FileHandle& operator=(FileHandle&&) noexcept = default;

private:
    int fd_;
};

:::tip

完全明示がレビュー文化の中核

  • 許す責務 → =default
  • 許さぬ責務 → =delete

:::


良い実装例

class Socket {
public:
    explicit Socket(int fd) : fd_(fd) {}

    Socket(const Socket&) = delete;
    Socket& operator=(const Socket&) = delete;

    Socket(Socket&&) noexcept = default;
    Socket& operator=(Socket&&) noexcept = default;

private:
    int fd_;
};

良い設計理由

  • 所有権移譲のみを許可
  • 二重所有を静的封止
  • レビュー者が設計意図を即理解可能

レビュー観点

レビューアーは以下を確認する。

  • コピー/ムーブ全4種が明示定義されているか
  • 所有権設計と一致しているか
  • delete適用は両系統(構築+代入)で一貫しているか
  • default使用箇所は設計理由が説明可能か
  • move専用型でもdeleteがセットで書かれているか

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

class Logger {
public:
    Logger() = default;

    Logger(const Logger&) = delete;
};
@Reviewer
コピー代入演算子もdelete指定が必要です。不完全禁止です。

改善例

Logger& operator=(const Logger&) = delete;

ケース2: ムーブ設計でコピー禁止が抜け

class Connection {
public:
    Connection(Connection&&) noexcept = default;
};
@Reviewer
ムーブ設計ならコピー禁止(delete)も併記してください。

改善例

Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;

ケース3: 実質所有権型で禁止漏れ

class FileHandle {
public:
    explicit FileHandle(int fd) : fd_(fd) {}
};
@Reviewer
所有権型ではコピー禁止(delete)を必ず明示してください。

改善例

FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;

ケース4: 空実装でdefault使わず

class Point {
public:
    Point() {}
};
@Reviewer
defaultで意図明示してください。

改善例

Point() = default;

ケース5: C++03流 private禁止残存

class Task {
private:
    Task(const Task&);
    Task& operator=(const Task&);
};
@Reviewer
C++11以降はdeleteで統一してください。

改善例

Task(const Task&) = delete;
Task& operator=(const Task&) = delete;

ケース6: default/delete理由が可視化不足

class Manager {
public:
    Manager(const Manager&) = delete;
};
@Reviewer
なぜコピー禁止か設計理由をコメント付記してください。

改善例

// シングルトン設計のためコピー禁止
class Manager {
public:
    Manager(const Manager&) = delete;
    Manager& operator=(const Manager&) = delete;
};

コピー/ムーブ構築・代入レビューの設計防止効果

防止対象 設計効果
所有権混乱 二重管理封止
API誤用 呼び出し側設計強制
継承事故 不正継承防止
保守崩壊 拡張安全保証

観点チェックリスト


まとめ

レビューアーの思考は常にこう整理する:

「この型の資源責務転送ルールは明示されているか?」

  • default/delete文化は設計意図自己説明言語
  • コピー/ムーブは所有権設計を読み取る起点

「暗黙生成放置ゼロ文化」こそC++レビュー技術の礎です。