DEV Community

Игорь Шевченко
Игорь Шевченко

Posted on

Understanding `union` vs `std::variant` in C++

💡 Understanding union vs std::variant in C++ (from low-level to modern safety)

Let’s start with a classic C++ concept: union.

A union allows multiple members to share the same memory location. Instead of allocating space for each field, it uses only enough memory for the largest one.

union Value {
    Node* p;
    int i;
};
Enter fullscreen mode Exit fullscreen mode

Here:

  • p and i occupy the same address
  • The union size = max(sizeof(Node*), sizeof(int))

👉 This is efficient — but comes with a cost.


⚠️ The Problem

A union does not track which value is active.

Value v;
v.i = 42;

// Later...
std::cout << v.p; // ❌ Undefined behavior
Enter fullscreen mode Exit fullscreen mode

You’re responsible for remembering what’s stored. That’s why we often add a manual tag:

enum class Type { ptr, num };

struct Entry {
    Type t;
    Value v;
};
Enter fullscreen mode Exit fullscreen mode

And use it like:

if (entry.t == Type::num)
    std::cout << entry.v.i;
Enter fullscreen mode Exit fullscreen mode

This pattern is called a tagged union.


🧠 The Modern C++ Solution: std::variant

C++ now gives us a safer, cleaner alternative:

#include <variant>

using Value = std::variant<Node*, int>;
Enter fullscreen mode Exit fullscreen mode

Now:

  • The type knows what it currently holds
  • No manual tag needed
  • No undefined behavior (if used correctly)

✅ Accessing the value safely

Value v = 42;

if (std::holds_alternative<int>(v)) {
    std::cout << std::get<int>(v);
}
Enter fullscreen mode Exit fullscreen mode

Or even better:

std::visit([](auto&& val) {
    std::cout << val;
}, v);
Enter fullscreen mode Exit fullscreen mode

🔄 Comparison

Feature union std::variant
Memory usage Minimal Slightly larger
Safety ❌ Manual ✅ Automatic
Type tracking ❌ External enum ✅ Built-in
Ease of use ⚠️ Error-prone ✅ Clean & expressive

🚀 Takeaway

  • Use union when you need low-level control and maximum efficiency
  • Use std::variant when you want type safety and clarity

In modern C++, the default choice should almost always be:

👉 std::variant over raw union

Top comments (0)