namespace cpp

C++ lernen, kennen, anwenden

Benutzer-Werkzeuge

Webseiten-Werkzeuge


kennen:include:variant

<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 ...
kennen/include/variant.txt · Zuletzt geändert: 2017-05-02 15:27 (Externe Bearbeitung)