SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

【C++の新機能を理解する】最新バージョンを徹底解説!

C++の新機能を理解する――列挙型と状態チェック関連の機能強化

【C++の新機能を理解する】最新バージョンを徹底解説! 第4回

状態チェック関連の強化

 C++ 23では、値の有無を表すstd::optionalに加えて、エラーの有無も表す新しい型std::expectedを使えるようになりました。また、std::optionalにモナド的操作のための新しい関数が追加されました。

 ここでいう状態チェックとは、値の有無や、エラー発生の有無を判定することです。C言語などでは、有効時(正常時)は値、無効時(エラー時)は0かNULLといったように状態チェックが行われてきましたが、以下の型を使うことで、安全に状態チェックが行えるようになりました。

  • std::optional<T>:値が存在するかを表す。値があれば値Tを取得でき、ないという状態も判定できる
  • std::expected<T, E>:値が正常かを表す。正常であれば値Tを取得でき、エラーであればその情報Eを取得できる

 このようにoptionalでは値の有無を扱いますが、expectedではエラーの有無とその情報まで扱うことができるので、エラー時の理由まで知りたいときにはexpectedを、値の有無さえ分かればよい場合にはoptionalを、と使い分けられます。

std::optionalによる状態チェック[C++ 17]

 std::optional型は、値の有無を表すための型です。処理結果そのものを型としてしまうことで、安全な値の取り扱いを可能にします。std::optional型を使わない場合、関数はある値を無効値と定めて、それを返すことで成否を表し、呼び出した側はそれをチェックする必要がありました。

リスト optional.cpp
int divide(int a, int b) {
  return (b == 0)? INT_MIN : a / b;
}
…略…
auto result = divide(10, 0);
cout << ((result != INT_MIN)? to_string(result) : string("Divide by zero")) << endl;	// Divide by zero

 関数divideは、除数が0であればINT_MINを返し、そうでない場合は商を返します。この場合、無効値をINT_MINとしているわけですが、これは正しい商かもしれません。このように、特定の値を無効値とすると、正常値と区別が付かないということもあり得ますし、そもそも無効値であるかのチェックも行われない可能性があります。

 C++ 17から、値の有効/無効を表せるoptional型が利用できるようになりました。std::optional型を使うと、値の取得にも一定の手続きが必要になります。このため、値の判定を意識的に行うことをプログラマに強制することになります。

リスト optional.cpp
#include <optional>
…略…
optional<int> divide_opt(int a, int b) {
  return (b == 0)? nullopt : make_optional(a / b);
}
…略…
auto result_opt = divide_opt(10, 0);
cout << (result_opt? to_string(result_opt.value()) : string("Divide by zero")) << endl;	// Divide by zero

 関数は、optional型を返すものとして定義します。無効時にはnulloptを返し、有効時にはmake_optional関数でoptional型の値を生成して返します。nulloptは、optional型において有効な値を保持していない状態を表すために使用される定数です。呼び出し側では、bool演算子によってnulloptであるかを判定し、有効であればvalue関数で値を取り出してそれを出力し、無効であればその旨を出力します。

エラー処理を安全に扱える新しい型std::expected[C++ 23]

 C++ 23からは、optional型を発展させたexpected型を利用できるようになりました。expected型を使うと、値の有効/無効に加えて、エラー時にその理由などを表す値を指定することができます。

リスト expected.cpp
#include <expected>
…略…
expected<int, string> divide_exp(int a, int b) {	(1)
  if (b == 0) {
    return unexpected{"Divide by zero"};	(2)
  }
  return (a / b);
}
…略…
auto result_exp = divide_exp(10, 0);
cout << (result_exp? to_string(result_exp.value()) : result_exp.error()) << endl;	// Divide by zero	(3)

 こちらも、optional型と使い方はほぼ同じです。異なるのは、(1)のように型パラメータにエラー時の情報を表す型を追加すること、エラー情報を(2)のようにunexpectedクラスで生成すること、エラー情報は(3)のようにerror関数で取得することです。

std::optionalにおけるモナド的操作のためのand_then/transform/or_else[C++ 23]

 optional型には、C++ 23でand_then/transform/or_else関数が追加されました。これらは、「モナド的操作」のための関数です。

 モナド的操作とは、もともとは関数型プログラミングにおける概念で、簡単に表現すると値を連結操作する仕組みです。それぞれ、以下の機能となっています。引数には関数オブジェクトを指定し、値の処理はそこで定義します。

  • and_then:有効値なら別の処理を続ける
  • transform:有効値なら値を変換する
  • or_else:無効値なら代替処理を行う

 やや長いのですが、これらの関数の利用例を以下に示します。

リスト optional.cpp
// 引数の文字列をint型のOptionalに変換する
optional<int> stringToInt(const string& str) {	(1)
  try {
    return stoi(str);
  } catch (...) {
    return nullopt;
  }
}
// 引数の整数を2倍してint型のOptionalに変換する
optional<int> doubleValue(int value) {	(2)
  return value * 2;
}
// 引数の整数を文字列に変換する
string intToString(int value) {	(3)
  return "Value: " + to_string(value);
}
…略…
optional<int> opt = stringToInt("86");
// and_thenの例:有効値ならdoubleValueを呼び出して2倍する
auto result1 = opt.and_then(doubleValue);	(4)
cout << result1.value_or(0) << endl;	// 172
// transformの例:有効値なら10を加える
auto result2 = opt.transform([](int x) { return x + 10; });	(5)
cout << result2.value_or(0) << endl;	// 96
// or_elseの例:無効値なら9999のOptionalとする
auto emptyOpt = stringToInt("Invalid");	(6)
auto result3 = emptyOpt.or_else([] { return optional(9999); });
cout << result3.value_or(0) << endl;	// 9999
// 連結処理の例
auto final = opt	(7)
  .and_then(doubleValue)
  .transform(intToString);
if (final) {
  cout << final.value() << endl;	// Value: 172
}

 (1)~(3)は、and_then/transform関数に適用する関数の定義です。(1)は文字列から整数値への変換処理、(2)は整数値を2倍する処理、(3)は整数値の文字列化処理です。

 (4)~(6)は、and_then/transform/or_else関数の利用例です。それぞれ、以下の処理内容となります。

 なお、and_then関数とtransform関数は同じく値の加工のための関数に見えますが、and_then関数の引数はoptional型を返す関数、transform関数の引数はT(ここではint)型を返す関数である点が異なります。前者は、関数の戻り値となる新しいoptional型を生成し、後者は値そのものを書き換えて、それぞれ処理を継続するという違いがあります。

  • (4)and_then:正常値であればdoubleValue関数を呼び出して値を2倍する
  • (5)transform:正常値であればラムダ式で値に10を加える
  • (6)or_else:無効値であればラムダ式で9999をOptional型として返す

 いずれもOptional型を返すので、値の取得にはvalue関数およびvalue_or関数を呼び出します。value_orは、C++ 17から利用可能な、無効値のときに返す値を指定できる関数です。

 最後の(7)は、連結処理の例です。それぞれの関数がOptional型を返すので、値の2倍処理、文字列化を連結して行って結果を出力しています。

 なお、これらの関数はexpected型には最初から実装されているので、同様の利用が可能です。

まとめ

 今回は、C++ 23における標準ライブラリの機能強化について、列挙型関連、状態チェック関連を中心に紹介しました。

 次回は、引き続きC++ 23における標準ライブラリの機能強化について、出力関連、部分シーケンス関連、コレクション関連を中心に紹介します。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
【C++の新機能を理解する】最新バージョンを徹底解説!連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/24232 2026/05/20 08:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー