C ++ associative array with arbitrary value types

What is the best way to have an associative array with arbitrary value types for each key in C ++?

My current plan is to create a β€œvalue” class with member variables of the types that I will expect. For example:

class Value { int iValue; Value(int v) { iValue = v; } std::string sValue; Value(std::string v) { sValue = v; } SomeClass *cValue; Value(SomeClass *v) { cValue = c; } }; std::map<std::string, Value> table; 

The disadvantage of this is that you must know the type when accessing the "Value". i.e:.

 table["something"] = Value(5); SomeClass *s = table["something"].cValue; // broken pointer 

In addition, the more types that fit into the value, the more bloated the array will be.

Any best deals?

+7
c ++ arrays boost stl
source share
5 answers

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

+14
source share

Your approach was basically in the right direction. You will need to know the type in which you have invested. You can use boost::any and you can place anything on the map if you know what you have embedded:

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

Some answers recommend using boost::variant to solve this problem. But it will not allow you to store arbitrary typed values ​​on the map (as you wanted). You need to know the set of possible types. Given this, you can do it 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"]; 

This works because boost::variant overloads operator<< for this purpose. It is important to understand that if you want to save what is currently contained in this option, you still need to know the type, as in the case of boost::any :

 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 assignment order of a variant is a runtime property of the control flow of your code, but the type used by any variable is determined at compile time. Therefore, if you want to get a value from a variant, you must know its type. An alternative is to use visits as described in the variant documentation. It works because the variant stores code that tells it which type was last assigned to it. Based on this, he decides at runtime to overload the visitor that he uses. boost::variant quite large and not completely standard, and boost::any is standard, but uses dynamic memory even for small types (so slower. an option can use the stack for small types). Therefore, you must trade what you use.

If you really want to put objects in it that differ only in how they do something, polymorphism is the best way to go. You may have a base class from which you get:

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

To do this, you will mainly need a layout of this class:

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

boost::shared_ptr is the so-called smart pointer. It will automatically delete your objects if you delete them from your map, and nothing else refers to them. Theoretically, you could work with a simple pointer, but using a smart pointer will greatly increase security. Read the shared_ptr manual I'm attached to.

+9
source share

Subclass Value with IntValue , StringValue , etc.

+2
source share

Is it possible to use union with std :: map?

Boost :: variant provides countless variables.

You can make all of your Value data members confidential and provide accessors that return an error (or throw) if it is not installed.

+2
source share

The direct optimization will be the use of union , since you will always have only one of the values ​​as the key.

A more complete solution will encapsulate some information such as runtime in the interface. First of all, "What type is this?" and "How do you compare values ​​for equality?" Then use its implementation as a key.

+1
source share

All Articles