Boost.Variant bietet mit boost::variant
einen Typ an, der union
ähnelt. So können in einer boost::variant
-Variablen Werte unterschiedlicher Typen gespeichert werden. Zu jedem Zeitpunkt kann lediglich ein Wert gespeichert sein. Wird einer boost::variant
-Variablen ein neuer Wert zugewiesen, wird der alte überschrieben. Der neue Wert darf jedoch einen anderen Typ als der alte Wert haben. Einzige Voraussetzung ist, dass die verwendeten Typen als Template-Parameter an boost::variant
übergeben wurden und der entsprechenden boost::variant
-Variablen bekannt sind.
boost::variant
unterstützt beliebige Typen. So kann zum Beispiel ein std::string
in einer boost::variant
-Variablen gespeichert werden – etwas, was mit union
vor C++11 nicht möglich war. Mit C++11 wurden die Regeln für union
aufgeweicht, so dass ein std::string
Bestandteil einer union
sein kann. Da die Initialisierung der std::string
-Variablen in einer union
ein placement new erfordert sowie der Destruktor explizit aufgerufen werden muss, kann der Einsatz von boost::variant
auch in einer Entwicklungsumgebung, die C++11 unterstützt, sinnvoll sein.
boost::variant
in Aktion#include <boost/variant.hpp>
#include <string>
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
v = 'A';
v = "Boost";
}
Boost.Variant stellt eine Klasse boost::variant
zur Verfügung, die in der Headerdatei boost/variant.hpp
definiert ist. Da es sich bei boost::variant
um ein Template handelt, muss mindestens ein Template-Parameter angegeben werden. Der oder die Template-Parameter beschreiben, welche Typen unterstützt werden. Für Beispiel 24.1 bedeutet dies, dass in der Variablen v ein double
, ein char
oder ein std::string
gespeichert werden kann. Würden Sie versuchen, der Variablen v eine Zahl vom Typ int
zuzuweisen, würde der Code nicht kompilieren.
boost::variant
mit boost::get()
#include <boost/variant.hpp>
#include <string>
#include <iostream>
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
std::cout << boost::get<double>(v) << '\n';
v = 'A';
std::cout << boost::get<char>(v) << '\n';
v = "Boost";
std::cout << boost::get<std::string>(v) << '\n';
}
Um die in der Variablen v gespeicherten Werte auszugeben, greifen Sie wie im Beispiel 24.2 auf eine freistehende Funktion boost::get()
zu.
boost::get()
erwartet als Template-Parameter einen der Typen, die für die entsprechende Variable erlaubt sind. Geben Sie einen ungültigen Typ an, führt dies zu einem Laufzeitfehler. Es findet keine Überprüfung der Typen zur Kompilierung statt.
Wenn Sie lediglich Werte auf einen Stream wie die Standardausgabe ausgeben möchten, können Sie die Gefahr falscher Typen insofern umgehen, als dass Sie Variablen vom Typ boost::variant
direkt in einen Stream schreiben können. Sehen Sie sich dazu Beispiel 24.3 an.
boost::variant
auf einen Stream#include <boost/variant.hpp>
#include <string>
#include <iostream>
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
std::cout << v << '\n';
v = 'A';
std::cout << v << '\n';
v = "Boost";
std::cout << v << '\n';
}
Für einen typsicheren Zugriff auf in einer boost::variant
-Variablen gespeicherte Werte können Sie die Funktion boost::apply_visitor()
verwenden.
boost::variant
erstellen#include <boost/variant.hpp>
#include <string>
#include <iostream>
struct output : public boost::static_visitor<>
{
void operator()(double d) const { std::cout << d << '\n'; }
void operator()(char c) const { std::cout << c << '\n'; }
void operator()(std::string s) const { std::cout << s << '\n'; }
};
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
boost::apply_visitor(output{}, v);
v = 'A';
boost::apply_visitor(output{}, v);
v = "Boost";
boost::apply_visitor(output{}, v);
}
boost::apply_visitor()
wird als erster Parameter ein Objekt vom Typ einer Klasse übergeben, die von boost::static_visitor
abgeleitet sein muss. Die Klasse muss für jeden Typ der boost::variant
-Variablen, für die sie verwendet werden soll, den Operator operator()
überladen. Im Beispiel 24.4 muss dieser Operator entsprechend dreimal überladen sein, weil v die Typen double
, char
und std::string
unterstützt.
boost::static_visitor
ist ein Template. Der Typ des Rückgabewerts der überladenen Operatoren muss als Template-Parameter angegeben werden. Besitzen wie im Beispiel 24.4 die Methoden keinen Rückgabewert, wird kein Template-Parameter angegeben.
Der zweite Parameter, der an boost::apply_visitor()
übergeben wird, ist eine Variable vom Typ boost::variant
.
Wird boost::apply_visitor()
verwendet, wird automatisch der überladene Operator operator()
aufgerufen, der zum Typ des momentan in der boost::variant
-Variablen gespeicherten Werts passt. So wird im Beispiel 24.4 bei jedem Aufruf von boost::apply_visitor()
ein anderer überladener Operator aufgerufen – zuerst der für double
, dann der für char
und abschließend der für std::string
.
Der Vorteil von boost::apply_visitor()
ist, dass nicht nur automatisch der richtige Operator aufgerufen wird und der entsprechende Wert aus der boost::variant
-Variablen als Parameter übergeben wird. boost::apply_visitor()
stellt sicher, dass für alle von einer boost::variant
-Variablen unterstützten Typen Operatoren überladen wurden. Würde im Beispiel 24.4 eine der drei Methoden fehlen, könnte der Code nicht kompiliert werden.
Wenn wie im vorherigen Beispiel mehrere überladene Operatoren das Gleiche tun, kann wie im Beispiel 24.5 der Code mit Hilfe eines Templates vereinfacht werden.
boost::variant
#include <boost/variant.hpp>
#include <string>
#include <iostream>
struct output : public boost::static_visitor<>
{
template <typename T>
void operator()(T t) const { std::cout << t << '\n'; }
};
int main()
{
boost::variant<double, char, std::string> v;
v = 3.14;
boost::apply_visitor(output{}, v);
v = 'A';
boost::apply_visitor(output{}, v);
v = "Boost";
boost::apply_visitor(output{}, v);
}
Da bei boost::apply_visitor()
zur Kompilierung sichergestellt wird, dass der Code korrekt ist, sollte diese Funktion boost::get()
vorgezogen werden.