Using std::any to store values of any type

With std::optional you can represent some Type or nothing. With std::variant you can wrap several variants into one entity. And C++17 gives us one more wrapper type: std::any that can hold anything (any type) in an object, and reports errors (or throws exceptions) when you'd like to access a type that is not active.

A little demo:

std::any a(12);

// set any value:
a = std::string("Hello!");
a = 16;
// reading a value:

// we can read it as int
std::cout << std::any_cast<int>(a) << '\n';

// but not as string:
try
{
    std::cout << std::any_cast<std::string>(a) << '\n';
}
catch(const std::bad_any_cast& e)
{
    std::cout << e.what() << '\n';
}

// reset and check if it contains any value:
a.reset();
if (!a.has_value())
{
    std::cout << "a is empty!" << "\n";
}

// you can use it in a container:
std::map<std::string, std::any> m;
m["integer"] = 10;
m["string"] = std::string("Hello World");
m["float"] = 1.0f;

for (auto &[key, val] : m)
{
    if (val.type() == typeid(int))
        std::cout << "int: " << std::any_cast<int>(val) << "\n";
    else if (val.type() == typeid(std::string))
        std::cout << "string: " << std::any_cast<std::string>(val) << "\n";
    else if (val.type() == typeid(float))
        std::cout << "float: " << std::any_cast<float>(val) << "\n";
}

A true variable type in C++? If you like JavaScript then you can even make all of your variables std::any and use C++ like JavaScript :)

Main Features

Some of its Uses

While void* seems an extremely unsafe pattern with some limited use cases, std::any adds type-safety and thus has some real use cases. Such as

Still, in a lot of cases where we can limit the set of supported types std::variant would be a better choice. (Of course, it gets tricky when you implement a library without knowing the final applications, so you don’t know the possible types that will be stored in an object.)

Creation of std::any

There are several ways you can create std::any object:

You can see in the following example:

// default initialization:
std::any a;
assert(!a.has_value());

// initialization with an object:
std::any a2(10); // int
std::any a3(MyType(10, 11));

// in_place:
std::any a4(std::in_place_type<MyType>, 10, 11);
std::any a5{std::in_place_type<std::string>, "Hello World"};

// make_any
std::any a6 = std::make_any<std::string>("Hello World");

Changing the Value Held

When you want to change the currently stored value in std::any then you have two options: use member emplace or the assignment:

std::any a;

a = MyType(10, 11);
a = std::string("Hello");

a.emplace<float>(100.5f);
a.emplace<std::vector<int>>({10, 11, 12, 13});
a.emplace<MyType>(10, 11);

Accessing The Stored Value

In order to read the currently active value in std::any you have essentially one option: std::any_cast. This function returns the value of the requested type if it's in the object.

This function template though is quite powerful, as it has many ways of using:

struct MyType
{
    int a, b;

    MyType(int x, int y) : a(x), b(y) { }

    void Print() { std::cout << a << ", " << b << "\n"; }
};

int main()
{
    std::any var = std::make_any<MyType>(10, 10);
    try
    {
        std::any_cast<MyType&>(var).Print();
        std::any_cast<MyType&>(var).a = 11; // read/write
        std::any_cast<MyType&>(var).Print();
        std::any_cast<int>(var); // throw!
    }
    catch(const std::bad_any_cast& e)
    {
        std::cout << e.what() << '\n';
    }

    int* p = std::any_cast<int>(&var);
    std::cout << (p ? "contains int... \n" : "doesn't contain an int...\n");

    MyType* pt = std::any_cast<MyType>(&var);
    if (pt)
    {
        pt->a = 12;
        std::any_cast<MyType&>(var).Print();
    }
}

As you see you have two options regarding error handling: via exceptions (std::bad_any_cast) or by returning a pointer (or nullptr). The function overloads for std::_any_cast pointer access is also marked with noexcept.