How can I implement a map with different data types as values?

心已入冬 提交于 2019-11-28 11:29:02

You want to use boost::variant:

std::map <std::string, boost::variant<typeX, typeY>>

Are typeX and typeY subclasses of a typeBase class ? If so, you could do a std::map<std::string,typeBase*> to store both typeX* and typeY* in the map.

With some metaprogramming you can easily build an heterogenous map which can store any type from a given set of types. Here is an example which does this, without type erasure nor the need to visit the values.

One way to implement a multi-type map is by using the nifty features of std::tuple in C++11, which allows access by a type key. You can wrap this to create access by arbitrary keys. An in-depth explanation of this (and quite an interesting read) is available here:

https://jguegant.github.io/blogs/tech/thread-safe-multi-type-map.html

This would probably be extreme overkill, but QT has a variable called QVariant which can be used to map to different types of (QT) variables.

Documentation here: http://qt-project.org/doc/qt-5.0/qtcore/qvariant.html

You need a type erasure.

Type erasure is a pattern that hides the underlying type, such known examples are boost::any, but keep in mind that boost any has a dynamic polymorphic behavior (dynamic dispatch at runtime). boost::variant on the other hand is another example and uses template metaprogramming techniques. see variant vs any

The simplest solution though, could be writing your own class type erasure with an enum for the underlying type.

If you don't want to use Boost, then I think building a class hierarchy as proposed by rom1504 makes sense. I would implement an abstract base class with func() as member function as follows:

class Base {
public:
    virtual void func() = 0;
};

void Base::func() {};

Then, I would turn your typeX and typeY data types into subclasses by deriving from Base as follows:

class typeX : public Base {
public:
    void func() { std::cout << "typeX::func()" << std::endl; };
    int i;  // Example for your original 'typeX' content.
};

class typeY : public Base {
public:
    void func() { std::cout << "typeY::func()" << std::endl; };
    std::string s;  // Example for your original 'typeY' content.
};

Here, the func() implementations would take the content of your corresponding global func() functions. Next, your map needs to store pointers to Base as value:

std::map<std::string, Base*> map;
map["a"] = &A;
map["z"] = &Z;

As a result, you can implement your loop as follows (using a C++11 range-based for loop):

for (auto const &list_item : list)
    map[list_item]->func();

Notes:

  • In case you use dynamically created instances of typeX and typeY, you should prefer to store smart pointers in the map, such as std::unique_ptr or std::shared_ptr, to ease memory management.

  • If you have to stick to your global func() functions, then you can call them from the corresponding member functions. I would just turn the parameter into a pointer or reference to avoid copying objects.

Full code on Ideone

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!