variant[meta header]
std[meta namespace]
class template[meta id-type]
cpp17[meta cpp]
namespace std {
template <class ... Types>
class variant ;
}
variantクラスは、格納されうる候補の型リスト (Types...) に含まれる型のオブジェクトを切り替えながら保持する記憶域型である。継承関係にない複数の型を、単一のオブジェクトに代入・切り替えができる。
これは、継承によって基底クラスのオブジェクトに派生クラスのオブジェクトを代入できることと同様に、候補として列挙した複数の型のオブジェクトを単一の型のオブジェクトに代入して扱えるという点で、多態性を表現している。インタフェースが異なる複数の型の場合はビジター関数オブジェクトによって型ごとの動作を定義でき、インタフェースが共通の型の場合は、関数テンプレートの関数呼び出し演算子をもつ関数オブジェクトをビジターにすることで共通の動作をさせることができる。
// 継承関係にないクラス群
struct A { void f () {} };
struct B { void f () {} };
struct C { void f () {} };
// A, B, Cのいずれかの型を代入できる型
std::variant<A, B, C> v = A{}; // A型のオブジェクトを代入
v = B{}; // B型のオブジェクトに切り替え
// B型オブジェクトを保持しているか
if (std::holds_alternative<B>(v)) {
// 保持しているB型オブジェクトを取得
B& b = std::get<B>(v);
}
// どの型が代入されていたとしても、共通のインタフェースを呼び出す
std::visit ([](auto & x) {
x.f ();
}, v);
std::holds_alternative[link holds_alternative.md]
std::visit[link visit.md]
std::get[link variant/get.md]
このクラスと同様のことは共用体を使用しても達成できるが、このクラスはより使いやすいよう設計されている。
このクラスは追加の動的メモリ確保は行わず、保持するオブジェクトを自身のオブジェクト表現内に直接割り当てる。
このクラスはBoost Variant Library を元に設計されている
Boost Variant Libraryは、recursive variantによって再帰的なデータ構造を扱えるが、現時点のstd::variantクラスではそのようなデータ構造は扱えない
これは、JSONデータ形式のように値として数値・文字列・配列などを設定でき、配列の要素にもまた数値・文字列・配列などを設定できる、というようなデータの読み込み、書き込みで必要となる
Boost Variant Libraryは、「決して空にならない保証 ("Never-Empty" Guarantee) 」を提供しており、たとえ代入中に例外が発生したとしても、候補型のいずれの型も代入されていない状況が起こらないよう設計・実装されていた。標準ライブラリに導入されたこのクラスは、代入中に例外が発生した場合に空になる可能性をもっている
このクラスは、他の言語で「代数データ型 (Algebraic data type)」「直和型 (Union type, Sum type)」「タグ付き共用体 (Tagged union)」と呼ばれる機能の一部を表現できる。また、Either型として近しい機能が提供されている場合もある
Types...の全ての型が、std::destructible 要件を満たすこと
コンストラクタや代入の制約として、variant<std::string, std::string>のように、Types...内に同じ型が複数回現れる指定をする場合は、型のインデックスを指定する形式の機能のみ使用できる
こういった指定は、正常データかエラーデータどちらかが代入されるオブジェクトを用意する状況で、正常データとエラーデータがどちらも文字列、という場合に必要になる
名前
説明
対応バージョン
operator=
代入演算子
C++17
emplace
要素型のコンストラクタ引数から直接構築する
C++17
swap
他のvariantオブジェクトとデータを入れ替える
C++17
名前
説明
対応バージョン
visit
variantオブジェクトが現在保持している型に対応する関数を呼び出す
C++26
名前
説明
対応バージョン
get
保持している値を取得する
C++17
get_if
保持している値を指すポインタを取得する。エラー時にヌルポインタを返す
C++17
名前
説明
対応バージョン
visit
variantオブジェクトが現在保持している型に対応する関数を呼び出す
C++17
名前
説明
対応バージョン
swap
2つのvariantオブジェクトを入れ替える
C++17
名前
説明
対応バージョン
template <class T> struct hash;
hashクラスの先行宣言
C++17
template <class ...Types> struct hash<variant<Types...>>;
hashクラスのvariantに対する特殊化
C++17
#include < iostream>
#include < variant>
#include < string>
int main ()
{
// int, char, std::stringのいずれかの型の値を保持できる型
std::variant<int , char , std::string> v = 3 ; // int型の値を代入
// 候補型の0番目の型 (int) を保持しているか
if (v.index () == 0 ) {
int & x = std::get<0 >(v); // 型のインデックスを指定して、保持している値を取得
std::cout << x << std::endl;
}
v = std::string (" Hello" ); // std::string型オブジェクトを代入
// std::string型を保持しているか
if (std::holds_alternative<std::string>(v)) {
std::string& x = std::get<std::string>(v); // 型を指定して、保持している値を取得
std::cout << x << std::endl;
}
}
v.index()[link variant/index.md]
std::holds_alternative[link holds_alternative.md]
std::get[link variant/get.md]
variantを継承して操作を追加する (C++23)
#include < iostream>
#include < variant>
#include < string>
// variantを継承し、独自の操作を追加する
struct Value : std::variant<int , std::string> {
using variant::variant;
bool is_string () const
{
return std::holds_alternative<std::string>(*this );
}
};
int main ()
{
Value v = std::string (" Hello" );
std::cout << std::boolalpha << v.is_string () << std::endl;
// C++23から、継承した型もstd::visit()に渡せる
std::visit ([](const auto & x) { std::cout << x << std::endl; }, v);
}
std::holds_alternative[link holds_alternative.md]
std::visit[link visit.md]