C++/標準ライブラリ/optional
はじめに
[編集]<optionalヘッダーでは、std::optional型が定義されています。
std::optional型は、値が存在する場合とそうでない場合の両方を表現できる型です。従来、ポインタがNULLかどうかで値の有無を判断していましたが、std::optional型を使えば、安全で明示的な方法で扱えます。
nulloptは、std::optional型の値が初期化されていない状態を表す特殊な値です。
optional型
[編集]コンストラクタ
[編集]- デフォルトコンストラクタ
optional<T> opt;は値を持たない状態で初期化します nulloptを渡すコンストラクタoptional<T> opt(nullopt);も同様に値を持ちません- コピーコンストラクタ
optional<T> opt(opt2);があり、ムーブコンストラクタもあります in_placeコンストラクタoptional<T> opt(in_place, args...);を使うと直接値を初期化できます
デストラクタ
[編集]デストラクタ ~optional() は、もし値を持っていれば適切に破棄します。
代入演算子
[編集]代入演算子 operator= は、コピー代入、ムーブ代入、値による直接代入などがあります。
emplace関数
[編集]emplace関数を使うと、in_placeコンストラクタと同様に直接値を構築できます。
スワップ
[編集]swap関数で、2つのoptionalオブジェクトを効率的に入れ替えられます。
値アクセス関数
[編集]operator*やoperator->を使って、optionalが値を持っている場合にアクセスできます。value関数で値をコピーで取得できますが、値がない場合に例外が送出されるので注意が必要です。
観測関数
[編集]has_valueで値の有無を、valuoe_orで値がない場合のデフォルト値を指定できます。operator boolで値の有無を判定できます。
モナディック演算
[編集]and_then、transform、or_elseといった、モナド的な演算が用意されています。関数の合成や値が存在しない場合の処理を表現できます。
リセット関数
[編集]reset関数で、optionalに格納されていた値を破棄し、値を持たない状態にできます。
例外クラス
[編集]bad_optional_accessは、value関数などで、値を持たないoptionalにアクセスしようとした場合にスローされる例外クラスです。
比較演算子
[編集]optionalには多数の比較演算子 == != < > <= >= <=> が定義されています。
optional同士の比較はその値の大小で比較されます。どちらかが値を持たない場合は、それより小さいものとみなされます。nulloptとの比較では、optionalが値を持てば非等値、持たなければ等値となります。- 通常の値との比較では、
optionalが値を持っていればその値と比較され、持たなければ常に非等値となります。
補助機能
[編集]make_optionalは、optionalオブジェクトを簡単に生成するためのヘルパー関数です。std::hashには、std::optional型に対する特殊化が用意されています。
使用例
[編集]optionalは、関数からエラー状態を返す場合や、データの有無を表す場合に使えます。モナディック演算を使えば、複数のoptional値の処理を連鎖させられます。
- エラー状態を表す例
optional<int> divide(int x, int y) { if (y == 0) { return nullopt; // 0除算エラーの場合は値なし } return x / y; } // ... auto result = divide(10, 2); if (result) { cout << "Result: " << *result << endl; // 値がある場合は出力 } else { cerr << "Error: Division by zero" << endl; // エラー処理 }
- この例では、
divide関数が0で除算されたらnulloptを返し、それ以外なら計算結果をoptional<int>として返します。呼び出し側ではoperator boolを使って値の有無を判定し、値がある場合はoperator*で中身にアクセスしています。 - データの有無を表す例
optional<string> get_name() { // ...ユーザ入力を受け取る処理... if (入力があった) { return "入力された名前"; } else { return nullopt; // 入力なしの場合 } } // ... auto name = get_name(); cout << "Your name is: " << name.value_or("(not entered)") << endl;
- この例では、
get_name関数がユーザの入力名前をoptional<string>で返します。呼び出し側ではvalue_orを使って、値がない場合のデフォルト値を指定しています。 - モナディック演算の
optional<int> maybe_int = /* ... */; auto squared = maybe_int.transform([](int x) { return x * x; }) .or_else([] { return optional<int>(0); });
- この例では、
transformでoptionalの中身に対して関数を適用し、or_elseで値がない場合の代替値を指定しています。このように、and_then、transform、or_elseを組み合わせて、optional値の処理をモナド的に記述できます。
optional型の実装
[編集]std::optional型は、value_typeの値へのポインタを内部に持っています。値がある場合はその領域を指し、ない場合はNULLです。最適化によりは、小さな型ではポインタ未使用の最適化がなされるでしょう。
まとめ
[編集]std::optional型は、値の有無を安全に表現できる型で、従来のポインタを使った手法に比べてはるかに安全性が高くなります。モナディック演算などの高度な機能もあり、多様な場面でoptionalを活用できます。