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
を活用できます。