What are the performance characteristics of std::variant
?
How does std::variant
perform compared to other ways of storing multiple types, like unions or inheritance? Are there any performance pitfalls to watch out for?
std::variant
is generally very efficient, offering performance characteristics similar to a union. The key performance features are:
- No heap allocation: Like a union, a variant stores its value directly in the variant object itself. This avoids the overhead of heap allocation and deallocation.
- Space efficiency: A variant only needs enough space to store the largest of its alternative types. It does not need the sum of the sizes of all types, as a naive approach like a struct with a member for each type would.
- Efficient access: Accessing the current value of a variant is typically just as efficient as accessing a member of a union. The
std::get
andstd::get_if
functions are generally implemented as constant-time operations.
In comparison to other approaches:
- Compared to a union, a variant has slightly more overhead due to the need to store a discriminator (to keep track of which type is currently held). But this overhead is typically very small.
- Compared to inheritance-based solutions (like a base class with derived classes for each type), a variant is much more space-efficient (as it doesn't need a vtable pointer), and it avoids the overhead of virtual function calls.
However, there are a few potential performance pitfalls to be aware of:
- Visitor overhead: If you use
std::visit
with a lambda that has a complex template parameter (likeauto&&
), this can lead to significant code bloat, as the lambda will be instantiated for each possible variant type. For maximum performance, prefer simple visitor functions. - Exception handling: Operations that could throw an exception (like
std::get
when the type doesn't match, or assignment/emplacement when the type's move/copy operations could throw) can be slower due to the need for exception handling. Avoid exceptions where possible for maximum performance. - Large types: If one of the variant's types is much larger than the others, this can lead to wasted space, as the variant will always allocate enough space for the largest type. In such cases, consider using a
std::unique_ptr
to the large type instead.
Despite these potential pitfalls, std::variant
remains a highly performant choice for type-safe value-based polymorphism in most cases, offering better space and time efficiency than alternatives like inheritance.
Constrained Dynamic Types using Unions and std::variant
Learn how to store dynamic data types in C++ using unions and the type-safe std::variant