Exploring std::any and std::variant in Modern C++

Madhawa Polkotuwa
3 min readJan 27, 2025

C++ | Tips

intro

With the evolution of C++, the introduction of <any> and <variant> in C++17 has provided powerful tools for developers to handle flexible type storage and type-safe alternatives. In this post, we'll dive into these two utilities, explore their differences, and understand how to effectively use them.

What is <any>?

<any> is part of the Standard Template Library (STL) and is designed for storing values of any type. It enables runtime type storage and retrieval, making it a highly flexible tool for scenarios where the type is not known at compile-time.

Key Features of <any>:

  • Can store values of any type.
  • Requires runtime type checks to retrieve the stored value.
  • Flexible but less performant than type-safe alternatives like <variant>.

How to Use <any>:

#include <iostream>
#include <any>

int main() {
std::any value;

// Store an integer
value = 2;
std::cout << "Stored integer: " << std::any_cast<int>(value) << "\n";

// Store a string
value = std::string("Hello, World!");
std::cout << "Stored string: " << std::any_cast<std::string>(value) << "\n";

return 0;
}
Stored integer: 2
Stored string: Hello, World!

In the above example, std::any holds both an integer and a string at different times. We use std::any_cast to safely retrieve the stored value. If you attempt to retrieve a value of the wrong type, std::bad_any_cast will be thrown.

When to Use <any>:

  • When you need to store values of arbitrary types.
  • When type safety is not a strict requirement.

What is <variant>?

<variant> is a type-safe alternative to unions introduced in C++17. Unlike <any>, <variant> can only hold one value at a time, but it must be one of a predefined set of types.

Key Features of <variant>:

  • Type-safe at compile time.
  • No runtime overhead for type checking.
  • Supports pattern matching using std::visit for clean handling of different types.

How to Use <variant>:

#include <iostream>
#include <variant>

int main() {
std::variant<int, std::string> var;

// Assign an integer
var = 10;
std::cout << "Integer value in variant: " << std::get<int>(var) << "\n";

// Assign a string
var = "Hello, Variant!";
std::cout << "String value in variant: " << std::get<std::string>(var) << "\n";

return 0;
}
Integer value in variant: 10
String value in variant: Hello, Variant!

Pattern Matching with <variant>:

One of the most powerful features of <variant> is pattern matching using std::visit. This allows you to handle each possible type cleanly:

#include <iostream>
#include <variant>

int main() {
std::variant<int, std::string> var = "Pattern Matching";

std::visit([](auto&& arg) {
std::cout << "Value: " << arg << "\n";
}, var);

return 0;
}

Here, std::visit automatically deduces the type currently stored in the variant and executes the appropriate logic.

When to Use <variant>:

  • When you need to store one value out of a predefined set of types.
  • When you need type safety and want to avoid runtime errors.

Key Differences Between std::any and std::variant

key diffrents of std::any and std::variant

Practical Use Cases

When to Choose <any>:

  • Logging frameworks that need to handle arbitrary types.
  • General-purpose containers that store heterogeneous data.

When to Choose <variant>:

  • Modeling state transitions in finite state machines.
  • Handling tagged unions in type-safe ways.
Youtube Video

Conclusion

Both <any> and <variant> are powerful tools introduced in C++17, each with its own strengths. Use <any> for flexibility when the types are not known at compile-time. Use <variant> for compile-time type safety and better performance. By understanding their differences and use cases, you can write more robust and maintainable C++ code.

--

--

No responses yet