What is std::any in C++?

Cengizhan Varlı
5 min readOct 14, 2023

--

std::any is a C++ Standard Library feature introduced in C++17. It is part of the C++ Standard Library's type-safe container classes and provides a way to store and manipulate values of any type in a type-safe manner. It can be particularly useful when you need to work with heterogeneous collections of objects or when the types of objects are not known at compile-time.

std::any is a wrapper class that is a nice alternative to void*, which we use as a general pointer. In codes written in C++ before the C++17 standards, the any class can be used in many places where the void* type was previously used. As you know, a pointer of type void* can hold the address of any type of object. A pointer of type void* does not know the type of the object it holds and cannot control its life. With C++17, std::any was provided as nice alternative to void*. Unlike void*, std::any controls the life of object it holds and always knows the type of the object it holds.

So where is std::any used? wherever void* is used.

std::any is not a class template. When we create a std::any object, we don't need to specify what type of value it will hold. An std::any object can hold a value of any type while also knowing the type of the value it contains. But how is this possible? How can an object store a value of any type? The secret lies in the fact that the std::any object also holds the type_info for the value it contains, in addition to the actual value it stores.

To use std::any you must include the <any> header file.The member functions, helper class and non-member functions are like follow;

a std::any object can be created to hold a value of a certain type or be empty :

#include <iostream>
#include <any>
#include <bitset>
#include <vector>

int main()
{
std::any any1 = 10; /* int */
std::any any2{ 1.3 }; /* double */
std::any any3{ "Cengizhan" }; /* const char* */
std::any any4{ std::string("Cengizhan") }; /* std::string*/
std::any any5{ std::vector<char>{'a','b','c'} }; /* std::vector */
std::any any6{}; /* empty */
std::any any7; /* empty */

return 0;
}

Just like std::optional, std::variant can be constructed by using std::in_place_type when creating the object. Similarly, the std::make_any<T> factory function can also be used.

std::make_any

Another way to create an object of type any is to use the make_any<> helper factory function.

auto any1{ std::make_any<std::string>("Cengizhan") };

Memory requirement for std::any objects

std::any<T> uses extra memory because it doesn’t know which type to use or assign. However, it uses the SBO (Small Buffer Optimization) method for performance gains, so sizeof (std::any) is more than the type kept and depends on the compilers’ Small Buffer Optimization implementation.

std::cout << "std::any sizeof: " << sizeof(std::any);

Output : “ std::any sizeof: 16 ”

My C++ compiler is gcc 11.4. When you run the above code you may get different values ​​depending on your compiler. Managing std::any memory requirements is a very dynamic process. The std::any type dynamically allocates and releases memory based on the size and type of data it stores.

How do we assign a new value to std::any object ?

The value of an any class object can be changed with the class’s assignment operator function or the emplace<> function.

#include <string>
#include <any>
#include <vector>

int main()
{
std::any any;
auto lmb = [](int x)->int{ return x*x; };

any = 123;
any = "cengizhan";
any.emplace<std::string>("Cengizhan");
any.emplace<std::vector<int>>(5);
any.emplace<decltype(lmb)>( lmb );
}

How to empty std::any?

To empty it, the class’s function named reset can be called:

any.reset();

With this call, if the variable a of type any is not empty, the life of the object held by the variable a is terminated. After this process, variable a is empty.

We can perform the emptying process by assigning the temporary object created with the default constructor.

any = {}; 

std::any observer functions

type() and has_value() function is observer functions of std::any.

We can check whether std::any object is empty or not with the has_value() function

#include <iostream>
#include <string>
#include <any>
#include <vector>

int main()
{
std::any any;
std::cout<<std::boolalpha<<any.has_value()<<"\n";

any = "cengizhan";
std::cout<<std::boolalpha<<any.has_value()<<"\n";

any.reset();
std::cout<<std::boolalpha<<any.has_value()<<"\n";
}
Output
const std::type_info& type() const noexcept;

The definition of type() function is like above.

With the type() function, the type of the object held by std::any can be learned.

int main()
{
std::any any;

std::cout << std::boolalpha<<(any.type() == typeid(void)) << '\n';

any = 15;

std::cout << std::boolalpha<<(any.type() == typeid(int)) << '\n';
}

The return value of the type() function is the std::type_info object, which carries the type information of the value held by the any object. If the any object is empty, the return value of the type function is typeid(void).

std::any_cast function

The std::any_cast function in C++ is used to safely extract and convert the stored value within a std::any object. std::any is a type-safe container that can hold values of any type. To access the value inside a std::any, you use std::any_cast.

#include <iostream>
#include <any>

int main() {

std::any any = 3;

try {
std::cout << "Value inside any: " << std::any_cast<int>(any) << std::endl;
}
catch (const std::bad_any_cast& e) {
std::cerr << "Error: " << e.what() << std::endl;
}

return 0;
}

What if the casting fails? Of course exception is thrown.

std::bad_any_cast

If the casting with any_cast<> fails, a exception which type bad_any_cast is thrown:

#include <iostream>
#include <any>

int main() {

std::any any = 3;

try {
std::cout << "Value inside any: " << std::any_cast<std::string>(any) << std::endl;
} catch (const std::bad_any_cast& e) {
std::cerr << "Error: " << e.what() << std::endl;
}

return 0;
}
Output

--

--