この記事のポイント

  • goto文をレビューで摘出する力を養う
  • 例外処理の理由説明が設計品質を左右する理由を理解する
  • レビューアーが制御構造と保守性を読む技術を身につける

C++は柔軟な制御構造を許しますが、
柔軟さゆえに設計崩壊を呼びやすいポイント も存在します。
goto文と例外理由の明示はその代表例です。


1. なぜgoto禁止と例外コメントが重要なのか?

1-1. goto使用が危険な理由

  • 制御構造が分岐と合流でねじれやすくなる
  • 後から読んでも意図が追いにくい
  • エラーハンドリングで乱用されやすい

1-2. 例外理由を明示すべき理由

  • 何を違反した結果の例外なのかが読めなくなる
  • 契約違反なのか環境依存なのか保守者が迷う
  • 例外文脈が曖昧になるとデバッグが困難化する

1-3. レビューアーは以下を確認する

チェック項目
gotoを使っていないか
gotoが本当に必要な場面か(通常必要ない)
throwの直前に理由説明があるか
例外種別(契約違反・実行時エラー)が整理されているか

2. 良い実装例:goto不使用・例外理由明示

#include <string>
#include <stdexcept>

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    std::string clientIp;
    int responseCode;
    std::string requestedAt;
};

class ApiRequestProcessor {
public:
    void process(const ApiRequestLog& log) {
        validate(log);
        executeRequest(log);
        logProcessing(log);
    }

private:
    void validate(const ApiRequestLog& log) {
        if (log.endpoint.empty()) {
            // エンドポイント未指定は契約違反
            throw std::invalid_argument("APIエンドポイントが指定されていません");
        }
        if (log.responseCode < 100 || log.responseCode > 599) {
            // HTTPレスポンスコード範囲外は契約違反
            throw std::invalid_argument("HTTPレスポンスコードが不正です");
        }
    }

    void executeRequest(const ApiRequestLog& log) {
        // リクエスト処理
    }

    void logProcessing(const ApiRequestLog& log) {
        // ログ記録処理
    }
};
  • 制御フローは通常の構造で整理
  • 例外は契約違反に限定し、理由を明記
  • 保守時の理解コストを下げている

3. 良くない実装例:goto使用+例外理由不足

#include <string>
#include <stdexcept>

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    std::string clientIp;
    int responseCode;
    std::string requestedAt;
};

class ApiRequestProcessor {
public:
    void process(const ApiRequestLog& log) {
        if (log.endpoint.empty()) {
            goto error;
@Reviewer
gotoは使用しないでください。制御構造の流れが読みにくくなります。
} if (log.responseCode < 100 || log.responseCode > 599) { goto error; } executeRequest(log); logProcessing(log); return; error: throw std::invalid_argument("invalid input");
@Reviewer
例外の理由説明が不足しています。契約違反の内容をコメントしてください。
@Reviewer
例外メッセージが曖昧です。invalidの具体的理由を書いてください。
} private: void executeRequest(const ApiRequestLog& log) { // リクエスト処理 } void logProcessing(const ApiRequestLog& log) { // ログ記録処理 } };

改善例

#include <string>
#include <stdexcept>

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    std::string clientIp;
    int responseCode;
    std::string requestedAt;
};

class ApiRequestProcessor {
public:
    void process(const ApiRequestLog& log) {
        validate(log);
        executeRequest(log);
        logProcessing(log);
    }

private:
    void validate(const ApiRequestLog& log) {
        if (log.endpoint.empty()) {
            // 契約違反: エンドポイント未指定
            throw std::invalid_argument("APIエンドポイント未指定");
        }
        if (log.responseCode < 100 || log.responseCode > 599) {
            // 契約違反: レスポンスコード異常
            throw std::invalid_argument("HTTPレスポンスコード不正");
        }
    }

    void executeRequest(const ApiRequestLog& log) { }
    void logProcessing(const ApiRequestLog& log) { }
};
  • 制御構造が整理され、例外理由も明確化
  • 保守・障害調査時に役立つ設計意図が残る

4. ケース別レビュー:goto誤用パターン

リソース開放管理でgotoに逃げる例

bool ApiRequestInitializer::initialize() {
    if (!loadConfig()) {
        goto error;
    }
    if (!connectDatabase()) {
        goto cleanupConfig;
    }
    if (!registerHandlers()) {
        goto cleanupDatabase;
    }
@Reviewer
gotoでリソース解放順を管理しないでください。RAII利用でスコープ自動解放を検討しましょう
return true; cleanupDatabase: disconnectDatabase(); cleanupConfig: unloadConfig(); error: return false; }

改善例:RAII適用

RAIIを使って解放処理を自動化することで、gotoを使わないようにします。
RAIIとは

class ConfigGuard {
public:
    ConfigGuard() : loaded(false) {}
    ~ConfigGuard() { if (loaded) unloadConfig(); }
    bool load() { loaded = loadConfig(); return loaded; }
private:
    bool loaded;
};

class DatabaseGuard {
public:
    DatabaseGuard() : connected(false) {}
    ~DatabaseGuard() { if (connected) disconnectDatabase(); }
    bool connect() { connected = connectDatabase(); return connected; }
private:
    bool connected;
};

bool ApiRequestInitializer::initialize() {
    ConfigGuard config;
    if (!config.load()) return false;

    DatabaseGuard db;
    if (!db.connect()) return false;

    if (!registerHandlers()) return false;

    return true;
}
  • goto不要
  • スコープで自然に解放
  • 例外安全性も上がる

5. チェックリストまとめ

観点 確認内容
goto排除 使用されていないか
制御構造 本来なら通常フローで表現可能ではないか
例外理由説明 throw直前に説明があるか
例外分類整理 契約違反と実行時例外を整理しているか
例外メッセージ 汎用的すぎないか
リソース解放設計 RAIIで管理されているか
例外安全性 途中例外でも後処理が漏れていないか

6. まとめ

goto禁止+例外理由明示レビューは、設計品質と保守性の基盤を守る重要観点です。

レビューアーは

  • 制御構造のねじれを摘出
  • 例外の文脈と理由を読み取る
  • 契約違反 vs 実行時異常を区別
  • RAII設計適用を促す

こうした読み取りが自然にできると、
設計レビューの質が一段高まります。