Polymorphism is the classic Object Oriented way to implement runtime dispatch, but virtual table lookups carry pointer dereference latencies. C++17 introduced type safe sum types via std::variant, providing a stack allocated, zero redirect alternative to class hierarchies.
Let's look at variant layouts, visitor operations, and polymorphism trade offs.
What are Sum Types? (std::variant)
A std::variant is a type safe replacement for standard C style unions. While a union allows you to store different types in the same memory space but does not track which type is currently active (making it easy to read garbage data), std::variant enforces strict safety by keeping a record of the active type.
Variant Memory Layout vs Virtual Tables
Under the hood, a std::variant is a tagged union. The compiler reserves inline stack space equal to the size of the largest alternative type, plus an internal index (type tag) that records which alternative is active. The index is at least one byte and is often padded for alignment, so the total size is not simply max(sizeof(Ts)...) + 1.
Because the memory is inline (no heap allocation for the variant object itself), there is no virtual pointer dereferencing, and access patterns are often more cache-friendly than pointer-chasing polymorphism. When combined with std::visit, the compiler can generate branch tables that are predictable on many workloads, measure before assuming they beat virtual dispatch in your hot path.
std::any and std::monostate
std::any (C++17) is a container that can hold a value of any* type. Unlike std::variant, which requires listing all possible types in advance, std::any dynamically manages any value, though it relies on heap allocation when values exceed small storage optimization limits.
* std::monostate is a helper type used with std::variant when you want a distinct empty state. Put it first when you need a default-constructible variant with no active value yet, for example, std::variant<std::monostate, int, std::string> v; starts empty.
* The Storage Locker Metaphor: Virtual polymorphism is like dialing a switchboard operator who redirects you to different offices (vptr jumps). std::variant is like opening a physical labeled storage locker. The locker has dedicated slots for a circle or a square. You inspect the label on the door (type tag) and pull the shape out directly with no middleman.