In order to save memory, unions allow you to use the same memory to store objects from a list of possible types as long as one object is stored at a time. Unions are not inherently safe, as they expect you to externally keep track of the type of value they currently hold.
Wrong tracking has the potential to corrupt memory or to trigger undefined behaviors.
A straightforward way to avoid it is storing the information about the currently active alternative along with the union. Here follow suggested patterns to do that:
This pattern uses the fact that when two alternatives of a standard layout union are standard-layout-structs that share a common initial sequence, it is allowed to read this common initial sequence on one alternative even if the other alternative is the one currently active. This is commonly used to limit the number of bits required to store the discriminant.
This pattern is more straightforward, and wraps the union inside a structure that will also store the discriminant. Note that in this case, the union itself can be anonymous.
----
// Pattern 3 (C++17)
using stdVariantPattern = std::variant<altType1, altType2>;
* Safer as the type of the current value is always known and checked before usage.
* More practical as it can have members of any type. It also supports redundant type which is useful when alternatives have the same type with different semantic meanings.
* Easier to use as it provides many member/helper functions.
On top of these advantages, std::variant is easier and safer when using alternatives with non-trivial types.
One noticeable difference with unions is that the alternatives in a std::variant do not have a name. You can access them by type or by index, using std::get (throws if the wrong alternative is accessed) or std::get_if (returns a null pointer if the wrong alternative is used). But very often, instead of accessing a specific alternative, visitors are used to distinguish cases of the variant.
This rule raises an issue when unions are used outside of the 3 suggested patterns.
== Noncompliant Code Example
----
void rawUnion() {
union IntOrDouble { // Noncompliant: union is not wrapped