はじめに
C++で、関数が処理に成功した場合は値を返し、失敗した場合は「値がない」ことを示したい、という場面は頻繁にあります。従来は、-1
のような特別な値(番兵)を返したり、ポインタでnullptr
を返したり、bool
の成否フラグを別途返したり、といった方法が取られていましたが、いずれも一長一短でした。
C++17では、この問題を解決する、よりモダンで型安全な方法として std::optional
が導入されました。optional
は、
- 有効な値
- 値がないことを示す特殊な状態
std::nullopt
のどちらか一方を保持します。これにより、「値が存在しない可能性」を、戻り値の型そのものに明確に表現できます。
【前提】C++17とは?
C++17は、2017年に正式化されたC++言語の規格です。std::optional
はこのC++17で追加された機能のため、利用するにはC++17に対応したコンパイラが必要です。
std::optional
を使ったサンプルコード
このコードは、文字列を整数に変換する関数 safe_stoi
を定義します。変換に成功すればint
型の値を持つoptional
を、失敗すれば値がない状態 (nullopt
) のoptional
を返します。
完成コード
#include <iostream>
#include <optional> // optional, nullopt
#include <string>
#include <charconv> // from_chars
using namespace std;
// 文字列を整数に変換する関数
// 戻り値: 成功ならoptional<int>, 失敗ならnullopt
optional<int> safe_stoi(const string& s) {
int result;
auto [ptr, ec] = from_chars(s.data(), s.data() + s.size(), result);
if (ec == errc()) {
// 1. 成功した場合: 値を返す
return result;
} else {
// 2. 失敗した場合: std::nulloptを返す
return nullopt;
}
}
int main() {
// --- 成功するケース ---
optional<int> res1 = safe_stoi("123");
// 3. bool値として評価し、値を持っているかチェック
if (res1) {
// 4. .value()または*で値にアクセス
cout << "成功1: " << res1.value() << endl;
cout << "成功1 (アスタリスク): " << *res1 << endl;
}
// --- 失敗するケース ---
optional<int> res2 = safe_stoi("abc");
if (!res2) {
cout << "失敗2: 変換に失敗しました。" << endl;
}
// 5. .value_or()で、値がない場合のデフォルト値を提供
cout << "失敗2 (.value_or): " << res2.value_or(0) << endl;
return 0;
}
コードの解説
1. optional<int> safe_stoi(...)
関数の戻り値の型を std::optional<int>
と定義しています。これは、「この関数は、int
型の値を返すかもしれないし、返さないかもしれない」という意味になります。
2. return result;
/ return nullopt;
- 成功時:
return result;
のように、有効な値を直接返します。optional
オブジェクトが暗黙的に構築されます。 - 失敗時:
std::nullopt
という特別な定数を返します。これにより、このoptional
が「値がない」状態であることが示されます。
3. if (res1)
optional
オブジェクトは、if
文などの条件式に置くと、有効な値を持っているかどうかをbool
値で評価できます。
- 有効な値を持っている場合 →
true
- 値がない (
nullopt
) 場合 →false
4. .value()
と *
optional
が有効な値を持っている場合に、その値にアクセスするには、.value()
メンバ関数か、ポインタのようにアスタリスク*
を使います。もし値がないオブジェクトに対してこれらを使うと、例外がスローされます。
5. .value_or(デフォルト値)
optional
が有効な値を持っていればその値を、持っていなければ引数で指定されたデフォルト値を返します。例外を投げないため、if
文なしで安全に値を取り出したい場合に便利です。
まとめ
今回は、C++17のstd::optional
を使って、値が存在しない可能性を型安全に表現する方法を解説しました。
std::optional<T>
: 値T
、または「値なし」(nullopt
)を保持する。- 失敗時に
std::nullopt
を返す。 - 呼び出し側では、
if
文で値の有無を判定し、.value()
や*
で安全に値を取得する。 .value_or()
を使えば、値がない場合のデフォルト値を指定できる。
optional
は、関数の戻り値として「成功したが結果は無効」といった状態を表現するのに最適で、エラーコードやマジックナンバー(番兵)を返す古いスタイルを置き換える、モダンなC++の機能です。