【问题标题】:How can I implement a map with different data types as values?如何实现具有不同数据类型作为值的映射?
【发布时间】:2014-02-18 12:29:37
【问题描述】:

我想将两种(不是更多)不同的数据类型作为值放入映射中,如下例所示:

typeX A, B, ...;
typeY Z, Y, ...;

void func (typeX) { ... }
void func (typeY) { ... }

std::map <std::string, what_to_put_here??> map;
map["a"] = A;
map["z"] = Z;
...

std::vector<std::string> list;
// This list will be something like "a", "y", ...

for (unsigned int i = 0; i < list.size(); ++i)
    func( map[list[i]] )

显然这不起作用,因为地图只接受一种数据类型的值。然而,当循环list 时,对func() 的调用应该是明确的,因为map[list[i]] 的类型是已知的。

我想避免显式转换或类型检查,例如:

if (typeid( map[list[i]] ).name() == "typeX")
    func( map[list[i]] )
else if (typeid( map[list[i]] ).name() == "typeY")
    func( map[list[i]] )

这可能吗?同样,它将仅限于两种不同的数据类型。

【问题讨论】:

  • 我会用两个成员变量制作包装类,并在地图声明中使用它。
  • boost::variant 可能会有所帮助。
  • 您是否考虑过将typeXtypeY 制作为公共基类的子类?

标签: c++ dictionary polymorphism stdmap


【解决方案1】:

你想使用boost::variant:

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

【讨论】:

  • 谢谢!我尝试使用它,但它在 func(map[list[i]]) 处给了我一个错误,因为 func() 显然不接受 boost::var。我必须明确类型检查吗?
  • 在调用func之前,您必须将对象显式提取为正确的类型
【解决方案2】:

typeX 和 typeY 是 typeBase 类的子类吗? 如果是这样,您可以使用std::map&lt;std::string,typeBase*&gt; 将 typeX* 和 typeY* 都存储在地图中。

【讨论】:

    【解决方案3】:

    通过一些元编程,您可以轻松构建一个异构映射,该映射可以存储给定类型集中的任何类型。 Here is an example 这样做,无需类型擦除,也无需访问值。

    【讨论】:

      【解决方案4】:

      实现多类型映射的一种方法是使用 C++11 中 std::tuple 的漂亮特性,它允许通过类型键进行访问。您可以包装它以通过任意键创建访问。在这里可以找到对此的深入解释(并且读起来很有趣):

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

      【讨论】:

        【解决方案5】:

        如果您不想使用Boost,那么我认为将类层次结构构建为proposed by rom1504 是有意义的。我将实现一个abstract base classfunc() 作为成员函数,如下所示:

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

        然后,我会将您的 typeXtypeY 数据类型通过从 Base 派生为子类,如下所示:

        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.
        };
        

        在这里,func() 实现将获取相应全局 func() 函数的内容。接下来,您的地图需要将指向Base 的指针存储为值:

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

        因此,您可以按如下方式实现循环(使用C++11 range-based for loop):

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

        注意事项:

          1234563 /p>
        • 如果你必须坚持你的全局func()函数,那么你可以从相应的成员函数中调用它们。我只是将参数转换为指针或引用以避免复制对象。

        Full code on Ideone

        【讨论】:

          【解决方案6】:

          这可能是极端的矫枉过正,但 QT 有一个名为 QVariant 的变量,可用于映射到不同类型的 (QT) 变量。

          此处的文档:http://qt-project.org/doc/qt-5.0/qtcore/qvariant.html

          【讨论】:

            【解决方案7】:

            您需要type erasure

            Type erasure 是一种隐藏底层类型的模式,例如boost::any,但请记住 boost any 具有动态多态行为(运行时动态调度)。另一方面, boost::variant 是另一个例子,它使用模板元编程技术。见variant vs any

            不过,最简单的解决方案可能是编写您自己的类类型擦除,并为基础类型使用枚举。

            【讨论】:

              猜你喜欢
              • 2012-03-06
              • 1970-01-01
              • 2019-03-17
              • 1970-01-01
              • 2015-06-27
              • 1970-01-01
              • 2012-08-30
              • 2021-12-22
              相关资源
              最近更新 更多