std::variant is a type-safe union that can hold one of several specified types. Since C++17 it offers a powerful alternative to classical inheritance for representing a value that may be one of a set of alternatives. In this article we will explore why std::variant is useful, how it works, and demonstrate common patterns and pitfalls through code examples.
1. Why Use std::variant?
| Traditional Polymorphism | std::variant |
|---|---|
| Requires a common base class | No common base needed |
| Virtual function overhead | Zero runtime cost beyond type tagging |
| Subclass proliferation | Simple to add or remove types |
| Potential for slicing | Guarantees correct type on access |
When you have a value that can be one of several discrete types, especially when the types are unrelated, std::variant keeps the type system explicit and compile‑time safe. It also eliminates the indirection and dynamic dispatch costs of virtual functions.
2. Basic Syntax
#include <variant>
#include <string>
#include <iostream>
#include <vector>
using Value = std::variant<int, double, std::string>;
int main() {
Value v = 42; // holds an int
v = 3.14; // holds a double
v = std::string{"Hello"}; // holds a string
std::visit([](auto&& arg){
std::cout << arg << '\n';
}, v);
}
- Definition:
using Value = std::variant<int, double, std::string>; - Construction: Implicit conversion from any alternative type.
- Access: `std::get (v)` or `std::visit`.
3. Common Operations
| Operation | Function | Example |
|---|---|---|
| Check type | `std::holds_alternative | |
(v)|if (std::holds_alternative(v)) {}` |
||
| Get value | `std::get | |
(v)|int i = std::get(v);` |
||
| Visit | std::visit(fn, v) |
See example above |
| Index | v.index() |
Returns 0‑based index of current alternative |
| Swap | std::swap(v1, v2) |
Swaps contents safely |
Error Handling
`std::get