Using std::variant
A std::variant is a closed-discriminated union. Variants simply have internal memory for maximum size of the underlying types plus a fixed overhead to manage which alternative is used. No heap memory is allocated. The resulting object has value semantics. Copying a variant is implemented as a deep-copy, it creates a new variant object with the current value of the alternative in its own memory.
#include <iostream>
#include <variant>
#include <string>
int main(){
// initialized with string alternative
std::variant<int, std::string> var{"hi"};
std::cout << var.index() << "\n";
// now holds int alternative
var = 42;
std::cout << var.index() << "\n";
try{
int i = std::get<0>(var); // access by index
int j = std::get<int>(var); //access by type
std::string s = std::get<std::string>(var); //error
}catch(const std::bad_variant_access& e){
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}Output:
The default constructor for std::variant always initializes the first type with the default constructor. If there is no default constructor for the first type, calling the default constructor for the variant is a compile-time error.
To support variants, where the first type has no default constructor, a special helper type is provided: std::monostate. Objects of type std::monostate always have the same state.