Unions and std::variant

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?

Illustration representing computer hardware

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.

Answers to questions are automatically generated and may not have been reviewed.

A computer programmer
Part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 124 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved