将多个类型存储到类成员容器中

时间:2018-03-08 01:46:52

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

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

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

class Storage {};

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

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

2 个答案:

答案 0 :(得分: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 ?
 }

但问题是,现在你有一个带有&#34; Hotdogs&#34;的元素。和&#34;价格&#34;地图中的键,但您没有关于值类型的信息。

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

答案 1 :(得分:0)

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

话虽如此,我在矢量上做这个,但原理也适用于地图。 如果您没有构建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;
}