C++1zから、型安全な共用体(type-safe union)の実装であるvariant
クラスが導入されます。
このクラスは、テンプレート引数で与えた候補型のリストに含まれる型のオブジェクトを代入できる型です。また、ビジター関数オブジェクトを使用することにより、現在代入されている型のオブジェクトを、安全に操作できます。
variant
クラスとその関連操作のために、<variant>
ヘッダが新設されます。
#include <iostream> #include <variant> #include <string> struct visitor { void operator()(int x) { std::cout << "int : " << x << std::endl; } void operator()(const std::string& x) { std::cout << "std::string : " << x << std::endl; } void operator()(double x) { std::cout << "double : " << x << std::endl; } }; int main() { // vには、int, std::string, doubleのいずれかが代入される。 // デフォルト構築では、0番目の型が値初期化される。 // ここではint()の値を持つ std::variant<int, std::string, double> v; v = 3; // int値を代入 v = "hello"; // 文字列を代入 v = 1.23; // double値を代入 // どの型が代入されているかに関わらず、共通の操作を適用する std::visit([](auto x) { std::cout << x << std::endl; }, v); // 代入されている型ごとに異なる処理を適用する std::visit(visitor(), v); // 代入されている値を取り出す // 指定した型の値が入っていなかったら例外が送出される try { double d = std::get<double>(v); } catch (std::bad_variant_access& e) { std::cout << e.what() << std::endl; } // 代入されている値を取り出す // 指定した型の値が入っていなかったらヌルポインタが返る if (double* p = std::get_if<double>(&v)) { } else { std::cout << "doesn't contains double value" << std::endl; } }
出力:
1.23 double : 1.23
std::variant
のデフォルトコンストラクタは、第1テンプレート引数に指定された型を値初期化します。variant
を空にしたい場合には、std::monostate
という空のクラスをvariant
の第1テンプレート引数に指定することになります。
そのほかに、variant
が不正な状態として空になる場合があります。これは、以下のように代入する型を変更する状況で、処理の内部で例外が発生した場合です:
struct S { operator int() { throw 42; }}; variant<float, int> v{12.f}; v.emplace<1>(S());
ここではemplace()
メンバ関数の中で例外が発生しますが、その際はvariant
オブジェクトは不正な状態となります。このような状態では、valueless_by_exception()
という述語メンバ関数がtrue
を返し、現在代入されている型のインデックスを取得するindex()
がstd::variant_npos
値を返すようになります。
Boostと標準の差異
Boost 1.61.0時点のvariant
と標準に予定されているものには、以下のような差異があります。
- Boostの
variant
には「決して空にならない保証 (Never empty guarantee)」がある。そのために、標準とは違い不正な状態というものにはならない - Boostの
variant
はビジターを適用するためにapply_visitor()
非メンバ関数を持つ。標準ではvisit()
非メンバ関数 - Boostの
variant
は代入されている型のインデックスを取得するwhich()
メンバ関数を持つ。標準ではindex()
メンバ関数 - Boostの
variant
には再帰的定義のためのrecursive_variant
があるが、標準にはない
参照
- N4218 Variant: a typesafe union
- N4450 Variant: a typesafe union (v2)
- N4516 Variant: a type-safe union (v3)
- N4542 Variant: a type-safe union (v4).
- P0088R0 Variant: a type-safe union that is rarely invalid (v5)
- P0088R1 Variant: a type-safe union that is rarely invalid (v6)
- P0088R2 Variant: a type-safe union for C++17 (v7)
- P0088R3 Variant: a type-safe union for C++17 (v8).
- P0393R3 Making Variant Greater Equal
お断り
この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。