Inhaltsverzeichnis
<variant>
Die Kapsel std::variant<Typen…>
bietet ab C++17 eine typsichere Alternative zu union
. Sie kann jeweils ein Objekt aus einer Liste von zur Übersetzungszeit bekannten Typen enthalten.
#include <variant> #include <iostream> std::variant<char, double> findAnswer(int question) { if (question == 42) return '*'; return double(question); }
Einpacken
Wird bei der Initialisierung kein Wert zugewiesen, gilt der erste Typ. Später kann jeder aufgelistete Typ zugewiesen werden:
std::variant<char, double> v; std::cout << "type index: " << v.index() << '\n'; // 0 v = '*'; v = 42.0; // v = 42; // Fehler: Typ int unzulässig v = findAnswer(6*9);
Auspacken
Der Zugriff auf das enthaltene Objekt erfolgt durch Angabe des Zieltyps oder die Indexnummer v.index()
in der Typenliste, hier 0=char
, 1=double
:
if (auto* ptr = std::get_if<char>(&v)) std::cout << "char: " << *ptr << '\n'; if (auto* ptr = std::get_if<0>(&v)) std::cout << "char: " << *ptr << '\n'; if (auto* ptr = std::get_if<1>(&v)) std::cout << "num: " << *ptr << '\n';
Bei Erfolg liefert die Abfrage mit std::get_if<Zieltyp>()
einen gültigen Zeiger des Zieltyps.
Bei bekannten Typ kommt std::get<Zieltyp>()
zum Einsatz:
if (std::holds_alternative<char>(v)) std::cout << "char: " << std::get<char>(v) << '\n'; else std::cout << "num: " << std::get<1>(v) << '\n';
Während die Angabe eines ungültigen Typs oder Typindexes einen Übersetzungsfehler erzeugt, kann der Zugriff auf einen anderen als den aktuell enthaltenen Typ eine Ausnahme werfen:
try { // std::get<3>(v); // Fehler: nur 0 und 1 zulässig std::get<char>(v); } catch(std::bad_variant_access& err) { std::cerr << err.what() << '\n'; }
Besucher
Das enthaltene Objekt kann mit einem Besucher inspiziert werden, der Funktionsoperatoren für die möglicherweise enthaltenen Typen bereitstellt:
struct AnswerVisitor { void operator()(char found) { std::cout << "char: " << found << '\n'; } void operator()(double found) { std::cout << "num: " << found << '\n'; } };
std::visit(AnswerVisitor{}, findAnswer(6*7));
Auch ein generischer Lambdaausdruck ist zulässig:
std::visit([](auto&& something){ std::cout << "found: " << something << '\n'; }, findAnswer('*') );
Leere Varianten
Besitzt ein (erster) Typ der Typliste keinen Standardkonstruktor, kann std::monostate
als erster Typ angegeben werden, um einer Variante keinen Anfangswert zuweisen zu müssen:
struct S { S(int i) : i(i) {} int i; }; std::variant<std::monostate, S> v; v = 42;
Objekt aus Parametern bauen
Objekte lassen sich aus deren Konstruktorparametern vor Ort erzeugen:
std::variant<int, std::string> v1(std::in_place_type<std::string>, 10, '-'); // Typangabe erforderlich std::variant<int, std::string> v2(std::in_place_index<1>, 10, '-'); // Index in Typliste std::variant<int, std::string> v3; v.emplace<std::string>(10, '-'); // nachträglich einsetzen std::cout << std::get<1>(v1) << '\n'; // v2, v3 ...