C++1z 統一的な有効値と無効値の表現をもつoptionalクラス

C++14で入りそうで入らなかったoptionalクラスですが、C++1zで入ることになりました。

optionalは、テンプレート引数で指定した型の値を有効値、std::nulloptという特殊なオブジェクトを無効値と見なす型です。

ひとつの型で有効値と無効値を表すために、intだったら負の値、ポインタだったらヌル、文字列だったら空文字列を無効値として使用されてきました。これは関数の仕様として無効値の範囲を定義する方法ですが、optionalクラスは型として無効値の表現を持ちます。

#include <iostream>
#include <optional>

int main()
{
    // 有効値3を代入
    std::optional<int> opt = 3;

    // 有効かを判定
    if (opt) {
        // 有効値を取り出す
        int& x = opt.value();
        std::cout << x << std::endl;
    }

    // 無効値を代入
    opt = std::nullopt;
    if (!opt) {
        std::cout << "nullopt" << std::endl;
    }
}

出力:

3
nullopt

これにより、関数を定義するプログラマが、有効値かエラーかのどちらかを戻り値として返したい場合には、値の仕様を考える必要なく、optionalを戻り値の型に設定すればよくなります。

optionalクラスは、新設される<optional>ヘッダで定義されます。

Boostと標準の差異

optionalクラスは、Boostで古くから提供されていた機能で、それをベースに標準の仕様が策定されました。標準の仕様が固まっていくにつれてBoostの方も合わせて機能が更新されていっているので、Boost 1.61.0時点では、この2つの差異はかなり小さくなっています。(標準の仕様を考えている人とBoost.Optionalのメンテナは同一人物です)

その前提で、現状の差異は以下のようになっています:

  • 標準は全面的にconstexpr対応している
  • Boostの無効値はboost::none、標準の無効値はstd::nullopt
  • 標準では、anyvariantとの共通設計のために、有効値を持つか判定するhas_value()メンバ関数make_optional()メンバ関数が定義される
  • 標準はoptional<T&>の部分特殊化が定義されない
  • 標準では入出力のストリーム演算子が定義されない (出力フォーマットの合意が得られなかった)
  • 標準はハッシュサポートがある

補足として、expectedクラスにあるようなモナドインタフェース(bind()map())や、0 or 1要素のRangeとして扱う機能はありません。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。