A union, prior to C++11, could only have class/struct members that had trivial constructors. This restriction has been removed in C++11. The unrestricted unions of C++11 can have data members that are not plain-old-data (POD) type .
Typically unions are used in a struct with an enum to store the data and its type. We have an example code below that defines an anonymous unrestricted union of 2 non-POD types (Tiger and Lion). Compiler would synthesize non-trivial special methods (e.g default constructor) for Tiger and Lion because they have std::string and std::vector data members:
// Tiger and Lion are non-POD types
struct Tiger {
std::vector<std::string> habitats;
// .. more attributes
};
struct Lion {
std::string habitat;
// .. more attributes
};
Note that If a struct contains a union that has a non-POD type, e.g BigCat below, the C++ compiler marks the special methods (e.g default constructor and destructor) deleted. That means at least a user-defined default constructor and destructor are must for BigCat to be of any use. A copy-constructor, copy-assignment-operator, move-constructor, move-assignment-operator might also have to be written depending on requirement.
// enum to store the kind of BigCat
enum class BigCatType { Tiger, Lion };
// struct BigCat has the unrestricted union
struct BigCat {
// Default constructor must be defined
// Note use of placement-new to call union member constructors
BigCat(BigCatType ct):bigCatType(ct) {
if( bigCatType == BigCatType::Tiger )
new (&tiger) Tiger();
else if(bigCatType == BigCatType::Lion)
new (&lion) Lion();
}
// Destructor must be defined
~BigCat() {
/* call tiger.~Tiger() or lion.~Lion()
depending upon bigCatType */
}
// Assignment operator
const BigCat& operator = (const BigCat& bc) {
/* ... can be implemented by
copying to tiger or lion depending upon bigCatType .. */
}
BigCatType bigCatType;
union {
Tiger tiger; // Only since C++11
Lion lion; // Only since C++11
};
};
Following code uses BigCat. A BigCat instance, bigCat, is created inside a block to make sure that it is destroyed before std::cout << tiger.habitats.front();
executes:
Tiger tiger;
Lion lion;
lion.habitat = "Africa";
{
// inside a block
BigCat bigCat(BigCatType::Tiger);
bigCat.tiger.habitats.push_back("Siberia");
tiger = bigCat.tiger;
// lion is copied to bigCat.lion
bigCat.lion = lion;
}
// Out of block. Print tiger's first habitat
std::cout << tiger.habitats.front();
What would be printed by or be the result of above code?