【C++23】std::expected の使い方 | 正常値とエラー値を返す新しい方法

目次

はじめに

C++で、成功すれば結果を、失敗すればエラー情報を返したい関数を設計する際、従来はエラーコードを別途引数で受け取る、std::optionalを使う、例外を投げる、などの方法がありました。

C++23では、この「成功または失敗」を表現するための、より優れた選択肢として std::expected が導入されました。std::expected は、以下のいずれかの値を保持します。

  • 正常に処理が完了した場合の「期待される (expected) 値
  • 処理が失敗した場合の「予期しない (unexpected) エラー値

これにより、関数の成功・失敗の意図が明確になり、呼び出し側でのエラーハンドリングも簡潔に記述できます。


【前提】C++23とは?

C++23は、2023年に正式化されたC++言語の比較的新しい規格です。std::expected はこのC++23で追加された機能のため、利用するにはC++23に対応した最新のコンパイラが必要です。


std::expected を使ったサンプルコード

このコードは、文字列を整数に変換する関数 string_to_int を定義します。変換に成功すればint型の値を、失敗すればエラーメッセージのstringを、std::expectedに格納して返します。

完成コード

#include <iostream>
#include <string>
#include <expected> // expected, unexpected
#include <charconv> // from_chars

using namespace std;

// 文字列を整数に変換する関数
// 戻り値: 成功ならint, 失敗ならstring
expected<int, string> string_to_int(const string& s) {
    int result;
    // from_charsで文字列から数値への変換を試みる
    auto [ptr, ec] = from_chars(s.data(), s.data() + s.size(), result);

    if (ec == errc()) {
        // 1. 成功した場合: 正常値を返す
        return result;
    } else {
        // 2. 失敗した場合: std::unexpectedでエラー値を返す
        return unexpected("変換に失敗しました。");
    }
}

int main() {
    // --- 成功するケース ---
    auto result1 = string_to_int("123");
    
    // 3. bool値として評価し、成功したかチェック
    if (result1) {
        // 4. .value()で正常値にアクセス
        cout << "成功1: " << result1.value() << endl;
    }

    // --- 失敗するケース ---
    auto result2 = string_to_int("abc");

    if (result2) {
        cout << "成功2: " << result2.value() << endl;
    } else {
        // 5. .error()でエラー値にアクセス
        cout << "失敗2: " << result2.error() << endl;
    }
    
    return 0;
}

コードの解説

1. expected<int, string>

std::expected のテンプレート引数として、expected<正常値の型, エラー値の型> を指定します。この関数は、「成功すればintを、失敗すればstringを返す」という意味になります。

2. return result; / return unexpected(...)

  • 成功時: return result; のように、正常値の型の値を直接返します。expectedオブジェクトが暗黙的に構築されます。
  • 失敗時: std::unexpected でエラー値のオブジェクトを作成して返します。これにより、この expected がエラー状態であることが示されます。

3. if (result1)

expectedオブジェクトは、if文などの条件式に置くと、正常値を持っているかどうかをbool値で評価できます。

  • 正常値を持っている場合 → true
  • エラー値を持っている場合 → false

4. .value()

expected が正常値を持っている場合に、その値にアクセスするには .value() メンバ関数を使います。もしエラー値を持っているオブジェクトに対して .value() を呼び出すと、例外がスローされます。

5. .error()

expected がエラー値を持っている場合に、その値にアクセスするには .error() メンバ関数を使います。


まとめ

今回は、C++23の std::expected を使って、関数の成功・失敗をエレガントに表現する方法を解説しました。

  • std::expected<T, E>: 正常値 T またはエラー値 E のどちらかを保持する。
  • 成功時は値を直接 return し、失敗時は std::unexpected でラップして return する。
  • 呼び出し側では、if文で成否を判定し、.value() で正常値を、.error() でエラー値を取得する。

std::expected は、エラーが関数の正常な振る舞いの一部である(例外的な状況ではない)場合に、例外処理のオーバーヘッドを避けつつ、型安全なエラーハンドリングを実現するための非常に優れたツールです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次