C++03 unions can only hold extremely simple types called Plain Old Data (POD). For example in C++03, you cannot store std::string
or std::vector
in a union.
Are you aware of the concept of unrestricted unions in C++11? Let me tell you about it briefly. C++11 relaxes requirements for unions, but you have to manage the construction and destruction of non POD types by yourself. You have to call in-place construction/destruction and remember what type is stored in a union. A huge amount of work, isn't it?
Can we have an unrestricted union like variable in C++03 that manages the object lifetime and remembers the type it has?
We'll be working with the header-only library, which is simple to use. Basic knowledge of C++ is all you need for this recipe.
Let me introduce the Boost.Variant
library to you.
- The
Boost.Variant
library can store any of the types specified at compile time. It also manages in-place construction/destruction and even does not even require the C++11 standard:
#include <boost/variant.hpp> #include <iostream> #include <vector> #include <string> int main() { typedef boost::variant<int, const char*, std::string> my_var_t; std::vector<my_var_t> some_values; some_values.push_back(10); some_values.push_back("Hello there!"); some_values.push_back(std::string("Wow!")); std::string& s = boost::get<std::string>(some_values.back()); s += " That is great!\n"; std::cout << s; }
Great, isn't it?
Boost.Variant
has no empty state, but has anempty()
function which is useless and always returnsfalse
. If you need to represent an empty state, just add some simple type at the first position of the types supported by theBoost.Variant
library. WhenBoost.Variant
contains that type, interpret it as an empty state. Here is an example in which we will use aboost::blank
type to represent an empty state:
void example1() { // Default constructor constructs an instance of boost::blank. boost::variant< boost::blank, int, const char*, std::string > var; // 'which()' method returns an index of a type // currently held by variant. assert(var.which() == 0); // boost::blank var = "Hello, dear reader"; assert(var.which() != 0); }
- You can get a value from a variant using two approaches:
void example2() { boost::variant<int, std::string> variable(0); // Following method may throw a boost::bad_get // exception if actual value in variable is not an int. int s1 = boost::get<int>(variable); // If actual value in variable is not an int will return NULL. int* s2 = boost::get<int>(&variable); }
The boost::variant
class holds an array of bytes and stores values in that array. The size of the array is determined at compile time by applying sizeof()
and functions to get alignment to each of the template types. On assignment, or construction of boost::variant
, the previous values are in-place destructed and new values are constructed on top of the byte array, using the placement new.
The Boost.Variant
variables usually do not dynamically allocate memory, and they do not require RTTI to be enabled. Boost.Variant
is extremely fast and used widely by other Boost libraries. To achieve maximum performance, make sure that there is a simple type in the list of supported types at the first position. boost::variant
takes advantage of C++11 rvalue references if they are available on your compiler.
Boost.Variant
is part of the C++17 standard. std::variant
differs slightly from theboost::variant
:
std::variant
is declared in the<variant>
header file rather than in<boost.variant.hpp>
std::variant
never ever allocates memorystd::variant
is usable with constexpr- Instead of writing
boost::get<int>(&variable)
, you have to writestd::get_if<int>(&variable)
forstd::variant
std::variant
can not recursively hold itself and misses some other advanced techniquesstd::variant
can in-place construct objectsstd::variant
hasindex()
instead ofwhich()
- The Using a safer way to work with a container that stores multiple chosen types recipe
- Boost's official documentation contains more examples and descriptions of some other features of
Boost.Variant
, and can be found at: http://boost.org/libs/variant - Experiment with the code online at http://apolukhin.github.io/Boost-Cookbook