C++ associative array with arbitrary types for values C++ associative array with arbitrary types for values arrays arrays

C++ associative array with arbitrary types for values


boost::variant seems exactly what you are looking for.


Your approach was basically into the right direction. You will have to know the type you put into. You can use boost::any and you will be able to put just about anything into the map, as long as you know what you put into:

std::map<std::string, boost::any> table;table["hello"] = 10;std::cout << boost::any_cast<int>(table["hello"]); // outputs 10

Some answers recommended the use of boost::variant to solve this problem. But it won't let you store arbitrary typed values in the map (like you wanted). You have to know the set of possible types before-hand. Given that, you can do the above more easily:

typedef boost::variant<int, std::string, void*> variant_type;std::map<std::string, variant_type> table;table["hello"] = 10;// outputs 10. we don't have to know the type last assigned to the variant// but the variant keeps track of it internally.std::cout << table["hello"];

That works because boost::variant overloads operator<< for that purpose. It's important to understand that if you want to save what is currently contained in the variant, you still have to know the type, as with in the boost::any case:

typedef boost::variant<int, std::string, void*> variant_type;std::map<std::string, variant_type> table;table["hello"] = "bar";std::string value = boost::get<std::string>(table["hello"]);

The order of assignments to a variant is a runtime property of the control flow of your code, but the type used of any variable is determined at compile time. So if you want to get the value out of the variant, you have to know its type. An alternative is to use visitation, as outlined by the variant documentation. It works because the variant stores a code which tells it which type was last assigned to it. Based on that, it decides at runtime which overload of the visitor it uses. boost::variant is quite big and is not completely standard compliant, while boost::any is standard compliant but uses dynamic memory even for small types (so it's slower. variant can use the stack for small types). So you have to trade off what you use.

If you actually want to put objects into it which differ only in the way they do something, polymorphism is a better way to go. You can have a base-class which you derive from:

std::map< std::string, boost::shared_ptr<Base> > table;table["hello"] = boost::shared_ptr<Base>(new Apple(...));table["hello"]->print();

Which would basically require this class layout:

class Base {public:    virtual ~Base() { }    // derived classes implement this:    virtual void print() = 0;};class Apple : public Base {public:    virtual void print() {        // print us out.    }};

The boost::shared_ptr is a so-called smart pointer. It will delete your objects automatically if you remove them out of your map and nothing else is referencing them them anymore. In theory you could have worked with a plain pointer too, but using a smart pointer will greatly increase safety. Read the shared_ptr manual i linked to.


Subclass Value with IntValue, StringValue, and so on.