Understanding the Use of std::any in C++ with an Example
Posted on In Programming, TutorialC++ std::any
is a type-safe container for single values of any type. It is useful to put multiple types into a single container such as std::vector
which requires all elements stored have the same “type”. It is a part of the C++17 standard library. This blog post will take a close look at a certain piece of code that uses std::any
and its related functions to handle various types in a vector.
Let’s break down the code piece by piece:
#include <any>
#include <iostream>
#include <vector>
#include <type_traits>
These are the standard library headers which are included:
<any>
: This header includes the std::any class, which is a type-safe container for single values of any type.<vector>
: This header includes thestd::vector
class template that represents a dynamic array. We will put multiplestd::any
objects into a vector.<type_traits>
: This header includes templates that can be used to make compile-time conditional decisions based on types.
class A {
public:
~A () {
std::cout << "A dstor\n";
}
A() {
}
A(const A&) = default;
};
This defines a simple class A with a default constructor, a copy constructor, and a destructor that outputs “A dstor” when called.
int main()
{
std::cout << std::boolalpha;
std::vector<std::any> vec;
std::cout << std::is_standard_layout<std::vector<std::any>>() << std::endl;
std::boolalpha
: This manipulator is used to print boolean values as “true” or “false” instead of “1” or “0”.std::vector<std::any> vec;
: This line declares a vector that can hold elements of any type.std::is_standard_layout<std::vector<std::any>>()
: This line checks ifstd::vector<std::any>
is of standard layout, meaning all non-static data members have the same access control, the class has no virtual functions and no virtual base classes.
std::any a = *(new int);
vec.push_back(a);
a = 9;
vec.push_back(a);
std::any a = *(new int);
: This line creates a new integer on the heap, dereferences it, and stores it in a variable of typestd::any
. However, this line leads to memory leak as there is no delete statement to deallocate the memory. A better approach would bestd::any a = int();
. We use thenew
here to illustratestd::any
can work well withnew
.vec.push_back(a);
: This line adds thestd::any
variable a to the vector vec.a = 9; vec.push_back(a);
: These lines assign an int value to a, and then add a to vec again.
a = A();
vec.push_back(a);
The std::any
variable a is assigned an instance of class A. It is also added to the vector vec.
OK, till now, we have added multiple elements of different types into the any vector. We can read them out and perform operations according to their types.
std::cout << "size: " << vec.size() << std::endl;
for (auto x : vec) {
std::cout << x.type().name() << " ";
std::any int_type = 8;
if (x.type() == int_type.type()) {
std::cout << std::any_cast<int>(x) << std::endl;
} else {
std::cout << "non int type" << std::endl;
}
}
vec.size()
prints the size of the vector vec. Then the for loop iterates over the vector vec.
For each element x:
x.type().name()
: This prints the name of the type of x.- Then do operation according to the type. We use
int
as an example.std::any int_type = 8;
declares a variable of typestd::any
and assigns it an int value. So we can use its type for type comparison.- If x is of type int,
std::any_cast<int>(x)
is used to retrieve the int value from x and print it. If x is not of type int, it prints “non int type”.
In summary, std::any
can be a super abstract class that can contain any types. This code demonstrates how std::any
can be used to store and manipulate values of any type in a type-safe way, and how to retrieve the value from an std::any
object with std::any_cast
. In the example code, we put elements of different types in a single vector, and then check the type during runtime and process the elements depending on their types.
Here is the C++ program putting all pieces together
#include <any>
#include <iostream>
#include <vector>
#include <type_traits>
class A {
public:
~A () {
std::cout << "A dstor\n";
}
A() {
}
A(const A&) = default;
};
int main()
{
std::cout << std::boolalpha;
std::vector<std::any> vec;
std::cout << std::is_standard_layout<std::vector<std::any>>() << std::endl;
std::any a = *(new int);
vec.push_back(a);
a = 9;
vec.push_back(a);
a = A();
vec.push_back(a);
std::cout << "size: " << vec.size() << std::endl;
for (auto x : vec) {
std::cout << x.type().name() << " ";
std::any int_type = 8;
if (x.type() == int_type.type()) {
std::cout << std::any_cast<int>(x) << std::endl;
} else {
std::cout << "non int type" << std::endl;
}
}
}
Here is one execution example
$ g++ -std=c++17 ./any.cpp -o any
$ ./any
true
A dstor
A dstor
size: 3
i 0
i 9
1A non int type
A dstor
A dstor
A dstor