Unions and std::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?

Illustration representing computer hardware

Both std::variant and std::any are used for storing values of different types, but they serve different purposes and have different characteristics.

std::variant is a type-safe union. It can hold a value of one of a fixed set of types, which are specified at compile-time as template arguments. The key characteristics of std::variant are:

  1. Type safety: You can only access the value in a variant through a visitor or by explicitly specifying the expected type. This prevents accessing data as the wrong type.
  2. Fixed set of types: The types a variant can hold are fixed at compile-time. You can't add new types at runtime.
  3. No heap allocations: variants usually store their values directly in the variant object (though some large types may be heap-allocated).

On the other hand, std::any is a type-erasing container. It can hold a single value of any type. The type of the value is erased and not known at compile-time. Key characteristics of std::any are:

  1. Runtime polymorphism: You can store any type in an any, even types that weren't known when the any was created.
  2. Type erasure: The type of the stored value is not known at compile-time. You need to cast the value back to its original type to use it.
  3. Heap allocation: any typically allocates the stored value on the heap.

Use std::variant when you have a fixed set of types and want type-safety and avoid heap allocations. For example:

#include <variant>
#include <iostream>

auto Visitor{[](auto&& arg) {
  constexpr bool isInt{std::is_same_v<
    std::decay_t<decltype(arg)>, int>};
  constexpr bool isString{std::is_same_v<
    std::decay_t<decltype(arg)>, std::string>};

  if constexpr (isInt) {
    std::cout << "int: " << arg << '\n';
  } else if constexpr (isString) {
    std::cout << "string: " << arg << '\n';
  }
}};

int main() {
  std::variant<int, std::string> v = 42;
  std::visit(Visitor, v);
}
int: 42

Use std::any when you need to store any type and only know what type you're storing at runtime. For example:

#include <any>
#include <iostream>

int main() {
  std::any a = 42;
  a = std::string("hello");
  std::string& s = std::any_cast<
    std::string&>(a);
  std::cout << s;  // prints "hello"
}
hello

In summary, use std::variant for type-safe value-based polymorphism, and std::any for type-erasing runtime polymorphism.

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:

  • 125 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