Using std::map stored in a std::any and then accessed via std::any_cast

Viewed 167

I am trying to understand the results from the following code:

template<typename KeyTypeT, typename ValueTypeT>
class MapWrapper {
    std::any _container;

    MapWrapper() {
        _container = std::map<KeyTypeT, ValueTypeT>();
    }

    void insert(KeyTypeT key, ValueTypeT value) {
       auto originalMap = std::any_cast<std::map<KeyTypeT, ValueTypeT>>(_container);
       std::cout << "count is " << originalMap.size() << std::endl;

       auto afterInsertion = std::any_cast<std::map<KeyTypeT, ValueTypeT>>(_container);
       afterInsertion.insert(std::make_pair(key, value));
       std::cout << "count is " << afterInsertion.size() << std::endl;

       auto originalMapAfterInsertion = std::any_cast<std::map<KeyTypeT, ValueTypeT>>(_container);
       std::cout << "count is " << originalMapAfterInsertion.size() << std::endl;


       _container = afterInsertion;
       auto originalMapAfterAssignement = std::any_cast<std::map<KeyTypeT, ValueTypeT>>(_container);
       std::cout << "count is " << originalMapAfterAssignement.size() << std::endl;
    }
};

The results are as follows:

Test Results:
count is 0
count is 1
count is 0 // why is this not 1?
count is 1

My question is why does _container not contain the newly inserted key until after I do an assignment?

Is std::any_cast returning a copy to the std::map<KeyTypeT, ValueTypeT>? I would have expected it to return the original map. Is there a way to modify the original map stored in the std::any?

2 Answers

The overload of any_cast that you are using (#2) gives you a copy of the value held by the any, not a reference to it. So the map that you're inserting into is a different map than the one the any is holding.

If you want to directly modify what the any is holding, you need to use one of the overloads that take the any by pointer (i.e. #5):

std::any a = 1;
int* p = std::any_cast<int>(&a); // <== like this
assert(p);
*p = 42;
assert(std::any_cast<int>(a) == 42);

Or you can explicitly provide a reference type into any_cast:

std::any a = 1;
std::any_cast<int&>(a) = 42; // <== int&, not int
assert(std::any_cast<int>(a) == 42);

Also note that even if any_cast itself wasn't giving you a copy, you're making one yourself by writing:

auto originalMap = ...;

instead of taking a reference to the result. So you probably wanted:

auto& map = std::any_cast<std::map<...>&>(_container);

Your any_cast is returning a copy - try return a pointer to the contained object or wrap in std::reference_wrapper

Related