Sometimes, you don't know what type a value is until program runtime. That's where std::any comes in handy. A std::any object can store a single value of pretty much any type. Later on, you can recover that value by telling it explicitly what type you're expecting.
A std::any object is empty when you default construct it. You place a value inside it via assignment or construction:
#include <iostream>
#include <any>
int main()
{
std::any a{ 42 };
std::cout << std::any_cast<int>(a) << '\n';
}
When a is first constructed with the integer value 42, then recovered by calling std::any_cast<int>(a), the value 42 is printed. The type given to any_cast must be the same as the type initially stored or the expression will throw an exception of type std::bad_any_cast.
It is possible to change the stored type later:
#include <iostream>
#include <any>
int main()
{
std::any a{ 42 };
a = 3.14;
std::cout << std::any_cast<double>(a) << '\n';
}
You can check if it has a value before calling std::any_cast:
#include <iostream>
#include <any>
int main()
{
std::any a;
// Check this condition before entering the block
if (!a.has_value())
std::cout << "empty\n";
}
If a type cast to a pointer like type does not succeed, then instead of an exception, a null pointer is returned:
#include <iostream>
#include <any>
int main()
{
std::any a{ 42 };
// Store the object's address in the pointer
if (int* p{ std::any_cast<int>(&a) })
std::cout << *p << '\n';
}
std::any effectively stores its value using type erasure. The actual type of the object held within std::any is hidden from the compiler, and must be explicitly provided by the programmer during extraction. If the incorrect type is specified during extraction, a null pointer, when casting to a pointer type, or an exception, std::bad_any_cast, is thrown.
The dynamic storage of arbitrary type with std::any is more efficient than using something like dynamic allocation from a pool of objects, but it has performance limitations: access speed is lower than with standard, correctly typed variables, and due to the lack of compile time type checking, errors can be introduced that a std::variant, which requires a predefined set of possible types, would catch earlier.
If the list of types your program is expected to hold is known and finite, the std::variant type should be preferred due to increased speed and earlier detection of type errors. Where type erasure is absolutely required during runtime, std::any will serve your purpose.
0 comments
No comments yet. Be the first.