potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++17 STL variant、any、optional

C++にはintdoubleといったC言語と共通の組み込み型(基本型)に加えてSTLの提供する型を使用できます。ここではSTLの提供する型のうち複数の型の値や無効値を一つの型としてまとめて扱う型variantanyoptionalを紹介します。

概要

クラス ヘッダー 概要 cpprefjpへのリンク
variant <variant> テンプレート引数で与えた型のいずれかの値を保持する型。空状態も指定可能。 リンク
any <any> どんな型の値でも保持できる型。 リンク
optional <optional> 元の型の値か統一的な無効値(nullopt)を保持する型。 リンク

variant

variantは扱う型を明示的に複数指定できます。std::stringstd::wstringを指定した場合は文字列("..."L"..."等)を代入してstd::wstring型の値を設定できます。ただし、anyは代入と取得(any_cast)が同じ型を要求することから厳格さはanyが勝ります。

以下はintdoublestd::wstringを保持できるstd::variant型でintstd::wstringを管理する例です。

#include <iostream>
#include <string>
#include <variant>

int main()
{
    std::variant<int, double, std::wstring> x(L"abcde");
    std::wcout << std::get<std::wstring>(x) << std::endl;
    // std::wcout << std::get<int>(x) << std::endl; // std::bad_variant_access

    x = 1234;
    // std::wcout << std::get<std::wstring>(x) << std::endl; // std::bad_variant_access
    std::wcout << std::get<int>(x) << std::endl;

    return 0;
}

テンプレート引数の最初にstd::monostateを指定することで空状態も表現できます。variantの初期値は最初のテンプレート引数の型のデフォルトコンストラクタから作成される ので、std::monospaceは最初のテンプレート引数に指定する必要があります。

#include <iostream>
#include <string>
#include <variant>

int main()
{
    std::variant<std::monostate, int, double, std::wstring> x;
    std::wcout << std::boolalpha << std::holds_alternative<std::monostate>(x) << std::endl; // true

    x = L"abcde";
    std::wcout << std::boolalpha << std::holds_alternative<std::monostate>(x) << std::endl; // false

    return 0;
}

any

anyは任意の型の値を保持できますが、保持する値の型と取得する型は同じ必要があります。したがって、Unicode文字列リテラルconst wchar_t*型を代入したany型変数からstd::wstringは取得できず、整数リテラル1int型を代入したany型変数からshort型は取得できません。異なる型の取得は例外を発生します。

#include <any>
#include <iostream>
#include <string>

int main()
{
    std::any x(L"abcde");
    std::wcout << std::any_cast<const wchar_t*>(x) << std::endl;
    // std::wcout << std::any_cast<std::wstring>(x) << std::endl; // std::bad_any_cast(同じ型ではないため)
    // std::wcout << std::any_cast<int>(x) << std::endl;          // std::bad_any_cast
    // std::wcout << std::any_cast<short>(x) << std::endl;        // std::bad_any_cast

    x = 1234;
    // std::wcout << std::any_cast<const wchar_t*>(x) << std::endl; // std::bad_any_cast
    // std::wcout << std::any_cast<std::wstring>(x) << std::endl;   // std::bad_any_cast
    std::wcout << std::any_cast<int>(x) << std::endl;
    std::wcout << std::any_cast<short>(x) << std::endl;             // std::bad_any_cast(同じ型ではないため)

    return 0;
}

optional

optionalは特定の型の値を保持するか無効状態を表せる型です。無効値を保持する場合、値の取得(value())は例外を発生します。

以下はoptionalint型の値か無効状態を保持する例です。

#include <iostream>
#include <string>
#include <optional>

int main()
{
    std::wcout.setf(std::wcout.boolalpha);

    auto x = std::make_optional(123);
    std::wcout << x.value() << std::endl;     // 123
    std::wcout << x.has_value() << std::endl; // true
    std::wcout << (bool)x << std::endl;       // true

    x.reset();
    // std::wcout << x.value() << std::endl;     // std::bad_optional_access
    std::wcout << x.has_value() << std::endl; // false
    std::wcout << (bool)x << std::endl;       // false

    return 0;
}

補足

これらの型の他、STLはメモリ管理、文字列、イテレーター、ビュー等多数の型を公開しています。また、Boostの導入でより多くの機能を使用できます。