【问题标题】:Storing multiple types into class member container将多种类型存储到类成员容器中
【发布时间】:2018-08-16 06:34:42
【问题描述】:

我正在阅读此 Q/A here,由于我的问题相似但不同,我想知道如何执行以下操作:

假设我有一个名为Storage 的基本非模板非继承类。

class Storage {};

我希望这个类有一个容器(无序多图)是我倾向于的地方......它将为变量类型 T 的名称 id 保存一个 std::string。类本身不会成为模板。但是,添加元素的成员函数将是。要添加的成员函数可能如下所示:

template<T>
void addElement( const std::string& name, T& t );

然后,此函数将填充无序多图。但是,每次调用此函数时,每种类型都可能不同。所以我的地图看起来像:

"Hotdogs", 8  // here 8 is int
"Price",  4.85f // here 4.8f is float.

如果类本身不是模板,我将如何使用模板、可变参数,甚至可能是元组、任何或变体来声明这样一个无序的多重映射?我不喜欢使用 boost 或标准以外的其他库。

我尝试过这样的事情:

class Storage {
private:
    template<class T>
    typedef std::unorderd_multimap<std::string, T> DataTypes;

    template<class... T>
    typedef std::unordered_multimap<std::vector<std::string>, std::tuple<T...>> DataTypes;
};

但我似乎无法让 typedef 正确,因此我可以这样声明它们:

{
    DataTypes mDataTypes;
}

【问题讨论】:

    标签: c++17 variadic stdtuple stdany unordered-multimap


    【解决方案1】:

    您标记了 C++17,因此您可以使用 std::any(或 std::variant,如果 T 类型可以是有限且已知的类型集)。

    存储值很简单。

    #include <any>
    #include <unordered_map>
    
    class Storage
     {
       private:
          using DataTypes = std::unordered_multimap<std::string, std::any>;
    
          DataTypes mDataTypes;
    
       public:
          template <typename T>
          void addElement (std::string const & name, T && t)
           { mDataTypes.emplace(name, std::forward<T>(t)); }
     };
    
    int main()
     {
        Storage s;
    
        s.addElement("Hotdogs", 8);
        s.addElement("Price", 4.85f);
    
        // but how extract the values ?
     }
    

    但问题是现在您在地图中有一个带有“Hotdogs”和“Price”键的元素,但您没有关于值类型的信息。

    因此,您必须以某种方式保存有关 th 值类型的信息(将 std::pair 中的值转换为某种 id 类型和 std::any?)以便在需要时提取它。

    【讨论】:

    【解决方案2】:

    我已经按照这些思路做了一些事情,实际的解决方案非常适合您的问题。

    话虽如此,我是在矢量上执行此操作,但该原理也适用于地图。 如果您没有构建 API,因此知道将涉及的所有类,您可以使用 std::variant 类似以下内容:

    #include <variant>
    #include <vector>
    #include <iostream>
    
    struct ex1 {};
    struct ex2 {};
    
    using storage_t = std::variant<ex1, ex2>;
    
    struct unspecific_operation {
        void operator()(ex1 arg) { std::cout << "got ex1\n";}
        void operator()(ex2 arg) { std::cout << "got ex2\n";}
    };
    
    int main() {
        auto storage = std::vector<storage_t>{};
        storage.push_back(ex1{});
        storage.push_back(ex2{});
        auto op = unspecific_operation{};
        for(const auto& content : storage) {
            std::visit(op, content);
        }
        return 0;
    }
    

    将输出:

    got ex1
    got ex2
    

    如果我没记错的话,使用std::any 将启用RTTI,这可能会变得相当昂贵;可能是错的。

    如果你提供更多关于你真正想用它做什么的细节,我可以给你一个更具体的解决方案。

    以无序地图为例:

    #include <variant>
    #include <unordered_map>
    #include <string>
    #include <iostream>
    
    struct ex1 {};
    struct ex2 {};
    
    using storage_t = std::variant<ex1, ex2>;
    
    struct unspecific_operation {
        void operator()(ex1 arg) { std::cout << "got ex1\n";}
        void operator()(ex2 arg) { std::cout << "got ex2\n";}
    };
    
    class Storage {
    private:
        using map_t = std::unordered_multimap<std::string, storage_t>;
        map_t data;
    public:
        Storage() : data{map_t{}}
        {}
    
        void addElement(std::string name, storage_t elem) {
            data.insert(std::make_pair(name, elem));
        }
    
        void doSomething() {
            auto op = unspecific_operation{};
            for(const auto& content : data) {
                std::visit(op, content.second);
            }
        }
    };
    
    int main() {
        auto storage = Storage{};
        storage.addElement("elem1", ex1{});
        storage.addElement("elem2", ex2{});
        storage.addElement("elem3", ex1{});
        storage.doSomething();
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-30
      • 1970-01-01
      • 2015-01-09
      • 2013-08-29
      • 2011-07-03
      • 2021-02-20
      相关资源
      最近更新 更多