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

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

When should I use std::variant instead of a union?
What are the advantages of using std::variant over a regular union in C++? When is it better to use std::variant?
How can I default construct a variant with non-default-constructible types?
If I have a std::variant with types that are not default-constructible, how can I still default construct the variant?
What is the difference between std::variant and std::any?
How does std::variant differ from std::any in C++? When would I use one over the other?
How do I handle errors when using std::variant?
What are the best practices for error handling when using std::variant in C++? How do I deal with exceptions and invalid accesses?
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?
How can I define a recursive variant type?
Is it possible to have a variant that contains a type that itself contains the same variant type? How would I define such a recursive variant?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant