C++ Learning

第61回 std::optional

「値があるかもしれないし、ないかもしれない」を型として表現する C++17 の標準ライブラリ。ポインタの nullptr、特殊値(-1 など)、例外に頼らない明示的な null 表現

このページで押さえること
✅ 最低限
  • std::optional<T> ― 値を「持つ/持たない」
  • opt.has_value() / if (opt) で存在確認
  • opt.value() / *opt で中身取得
  • 無い値は std::nullopt
⭐ 余裕があれば
  • value_or(default) ― 無ければデフォルト
  • C++23 の monadic operations(and_then など)
  • optional<reference> は不可(T& を包めない)
  • 戻り値としての活用

1. 何の問題を解決するか

C の流儀曖昧
// 見つからないとき -1? 他の特殊値? int find_index(const Data& d) { if (/* 見つかった */) return idx; return -1; // 規約? } // ポインタで null 返却? User* find_user(int id) { if (!found) return nullptr; // 所有権は? delete する? return ...; }
optional明示
#include <optional> std::optional<size_t> find_index(const Data& d) { if (/* 見つかった */) return idx; return std::nullopt; } auto idx = find_index(d); if (idx) { // 見つかった use(*idx); }

2. 基本操作

主要 APIoptional
std::optional<int> a; // 空 std::optional<int> b = 42; // 値あり std::optional<int> c = std::nullopt; // 明示的に空 // 存在確認 if (b.has_value()) { ... } if (b) { ... } // bool 変換 // 取り出し int x = *b; // 空だと UB int y = b.value(); // 空だと std::bad_optional_access 例外 // 空ならデフォルト値 int z = a.value_or(0); // a が空なら 0 // 書き換え a = 10; // 値を入れる a = std::nullopt; // 空にする a.reset(); // 空にする(同じ)
空のとき * や value() に注意: *opt は空でも実行できてしまい未定義動作。必ず if (opt) 等で確認してから使うか、value()(例外版)を使う。

3. 典型的な使い所

parse_int典型
std::optional<int> parse_int(std::string_view s) { try { return std::stoi(std::string{s}); } catch (...) { return std::nullopt; } } if (auto n = parse_int("42")) { std::cout << *n; // 42 }

4. ポインタ/参照との違い

optional は T の実体を値として包む(ポインタではない)。だからスコープで自動破棄され、リーク懸念なし。ただし T の参照は optional に入れられないoptional<T&> は未対応)。代わりに T* を包む、または std::reference_wrapper<T> を使う。

確認クイズ

Q1. 空の optional を表す定数は?

std::nullptr
std::null
std::nullopt
0
std::nullopt が optional 用の「値なし」。

Q2. optional が空のとき *opt はどうなる?

例外
未定義動作
0 が返る
nullptr
*opt / opt->member は空でも UB。例外が欲しいなら opt.value()

Q3. 空なら既定値を使う書き方は?

opt.default(0)
opt.value_or(0)
opt ? *opt : 0
opt.or(0)
②が答えだが、③も実質同等。value_or の方が簡潔でイディオム。

Q4. optional<T&> は使える?

使える
使えない(T* や reference_wrapper で代替)
C++20 以降のみ可
使えるが警告が出る
optional は参照型を直接は保持できません。T*std::reference_wrapper<T> を中に入れます。
← 前の講座
第60回 述語・集計