🌎 δΈ­ζ–‡ | English

Generalized Unions

C++11 introduced generalized (non-trivial) unions.

Union members share memory. The size of a union is at least large enough to hold the largest data member.

Why introduced?

  • Can directly hold objects like std::string, without needing pointers.
  • Better management of member lifetimes.

How Current Unions Differ from Before?

  • At most one variant member can have a default member initializer.
  • Unions can contain non-static data members with non-trivial special member functions.
union S {
  int a;
  float b;
  std::string str; // Before C++11, such members could not be placed directly, or static members were used.
  S() {}
  ~S() {}
}

I. Basic Usage and Scenarios

Usage of Ordinary Unions

Only one value is valid at a time.

union M {
  int a;
  double b;
  char *str;
}

Usage of Generalized Unions

The size of the union is the maximum space occupied by its data members, which changes dynamically based on the active member.

#include <iostream>
#include <string>
#include <vector>

union M {
  int a;
  int b;
  std::string str;
  std::vector<int> arr;
  M(int a) : b(a) { }
  M(const std::string &s) : str(s) { }
  M(const std::vector<int> &a) : arr(a) { }
  ~M() { } // Needs to know which data member is active to destruct correctly.
};

int main() {
  M m("123456");
  std::cout << "m.str = " << m.str<< std::endl;
  m.arr = { 1, 2, 3, 4, 5, 6 };
  std::cout << "m.arr = ";
  for(int v: m.arr) {
    std::cout << v << " ";
  }
  std::cout << std::endl;
  return 0;
}

Lifetime

A member's lifetime begins when it becomes active and ends when it becomes inactive.

#include <iostream>

struct Life {
  Life() { std::cout << "----Life(" << this << ") Start----" << std::endl; }
  ~Life() { std::cout << "----Life(" << this << ") End----" << std::endl; }
};

union M {
  int a;
  Life l;
  M(int n) : a(n) { }
  M(const Life &life) : l(life) { }
  ~M() { } // Needs to know which data member is active to destruct.
};

int main() {
  M m = 1;
  std::cout << "Life 1 time one Start" << std::endl;
  m = Life();
  // The Life object's lifetime ends before it becomes inactive here.
  std::cout << "Life 1 time one End" << std::endl;
  m = 2;
  std::cout << "Life 2 time one Start" << std::endl;
  m = Life();
  std::cout << "Life 2 time one Start" << std::endl;
  m = 3;
  return 0;
}

Anonymous Unions

int main() {
  union {
    int a;
    const char *b;
  };
  a = 1;
  b = "Jerry";
}

II. Precautions

Accessibility

Like struct, the default member access for a union is public.

Destruction of Unions

Destructors for unions are generally not defined because the union itself cannot know which member is active.

union M {
  char* str1;
  char* str2;
  ~M() {
    delete str1; // Error if the active member is str2.
  }
};

Limitations of Anonymous Unions

Anonymous unions cannot contain member functions or static data members.

union {
  int a;
  static int b; // Error: cannot have static data members.
  int print() {...}; // Error: cannot have member functions.
};

Undefined Behavior

Accessing an inactive member results in undefined behavior.

union M {
  int a;
  double b;
};

M m;
m.a = 1;
double c = m.b; // Error: undefined behavior.

III. Exercise Code

TODO

IV. Other