What is the difference between std::variant
and std::optional
?
How does std::variant differ from std::optional
in C++? When would I choose to use one over the other?
Both std::variant
and std::optional
are tools for representing values that may or may not be present, but they serve different purposes.
std::optional
is used to represent a value that may or may not exist. It's essentially a type-safe alternative to using a pointer to indicate the presence or absence of a value. An std::optional<T>
can either hold a value of type T
, or it can hold no value at all.
Here's an example:
#include <optional>
#include <iostream>
int main() {
std::optional<int> maybeInt = 42;
if (maybeInt.has_value()) {
std::cout << "Value: " << *maybeInt << '\n';
} else {
std::cout << "No value\n";
}
maybeInt = std::nullopt;
if (maybeInt.has_value()) {
std::cout << "Value: " << *maybeInt << '\n';
} else {
std::cout << "No value\n";
}
}
On the other hand, std::variant
is used to represent a value that can be one of several possible types. It's a type-safe union. A std::variant<T1, T2, ...>
holds a value that is one of the types T1
, T2
, etc.
Here's an example:
#include <variant>
#include <iostream>
using std::variant, std::holds_alternative,
std::string, std::get, std::cout;
int main() {
variant<int, string> v = 42;
if (holds_alternative<int>(v)) {
cout << "Int: " << get<int>(v) << '\n';
} else if (holds_alternative<string>(v)) {
cout << "String: " << get<string>(v) << '\n';
}
v = "hello";
if (holds_alternative<int>(v)) {
cout << "Int: " << get<int>(v) << '\n';
} else if (holds_alternative<string>(v)) {
cout << "String: " << get<string>(v) << '\n';
}
}
Int: 42
String: hello
So, the main difference is that std::optional
is used when you have a single type that may or may not have a value, while std::variant
is used when you have multiple possible types and the value is always one of those types.
Choose std::optional
when you have a value that may not exist, like the return value of a function that may not succeed, or a field in a struct that is not always filled in.
Choose std::variant
when you need to store a value that can be one of several different types, like a JSON value that could be a string, a number, a boolean, etc.
Of course, you can combine them: you can have a std::optional<std::variant<...>>
if you have a value that may or may not exist, and if it does exist, it could be one of several types.
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