C++で値がないかもしれない戻り値にstd::optionalを使う

C++で「値がないかもしれない」を表したいとき、std::optional が使えます。

たとえば検索して見つからなかった場合や、文字列を数値に変換できなかった場合です。

optionalを返す

#include <optional>
#include <string>
#include <vector>

std::optional<std::string> findName(int id) {
    if (id == 1) {
        return "alice";
    }
    return std::nullopt;
}

値がある場合はそのまま返し、ない場合は std::nullopt を返します。

呼び出し側は、値があるかを確認します。

auto name = findName(1);

if (name) {
    // *name で値を取り出す
}

value_orを使う

デフォルト値があるなら value_or() が使えます。

std::string name = findName(2).value_or("unknown");

見つからなかった場合は "unknown" になります。

例外とoptionalの違い

std::optional は、失敗が普通に起こる場合に向いています。

  • IDで検索して見つからない
  • 設定値が省略されている
  • 入力が空だった

こういうケースは、例外というより「値がない」として扱う方が読みやすいです。

一方で、ファイルが壊れている、前提条件が崩れている、復帰できない失敗なら例外の方が合う場合もあります。

ポインタとの違い

参照先がないことを表すだけならポインタでも書けます。

const User* findUser(int id);

ただ、戻り値として値そのものを返したい場合は std::optional<User> の方が意図が伝わります。

std::optional<User> findUser(int id);

所有権を返したい場合はスマートポインタを使います。所有権の話は C++でunique_ptrとshared_ptrを使い分ける目安 に分けています。

まとめ

std::optional は「値がないかもしれない」を型で表します。

  • 見つからない可能性がある戻り値に使う
  • 値なしは std::nullopt
  • デフォルト値には value_or()
  • 失敗が普通に起こるなら例外より読みやすいことがある
  • 所有権を返すならスマートポインタも候補

戻り値の意味を型に出したいときに、std::optional はかなり使いやすいです。

関連記事