具有可变大小和变量类型的c ++容器

时间:2012-05-12 19:07:47

标签: c++ list struct

我正在尝试创建类似列表的内容。但是,列表的不同实例可以具有不同数量的条目,并且条目的类型基于用户给出的输入。例如,用户声明他们希望列表中每个条目的结构包含int id,std :: string名称,double metricA和long metricB。根据此输入,将创建以下内容:

struct some_struct {
    int id;
    std::string name;
    double metricA;
    long metricB;
}

list<some_struct> some_list;

用户输入可以从文件中读取,在屏幕上输入等。另外,它们是some_struct中可变数量的条目。换句话说,它可能有上面列出的条目,它可能只有2个,或者它可能有10个完全不同的条目。有没有办法创建这样的结构?

此外,能够将比较运算符应用于some_struct的每个成员是必须的。我可以使用boost :: any来存储数据,但这会产生比较运算符的问题,并且还会产生比理想情况更多的开销。

3 个答案:

答案 0 :(得分:4)

C ++是一种强类型语言,这意味着您必须声明您的数据结构类型。为此,您不能声明具有任意数量或类型成员的struct,必须事先了解它们。

当然,现在有办法在C ++中处理这些问题。仅举几例:

  • 使用地图(std::mapstd::unordered_map)创建“表格”而不是结构。将字符串映射到字符串,即将名称映射到值的字符串表示,并将它们解释为您的心。
  • 使用预先制作的变体类型,例如boost::any
  • 使用多态 - 在列表中存储指向base的指针,并在值上调用虚拟机制调度操作。
  • 为您的输入语言创建类型系统。然后是每种类型的值表,并从列表中指向适当的表。

可能有很多其他方法可以做到这一点,因为有C ++程序员。

答案 1 :(得分:2)

有许多方法可以解决具有不同成员的数据结构问题,而这最好的方法很大程度上取决于它将如何使用。

最明显的是使用继承。您可以从基类中获得所有可能性:

struct base_struct { 
    int id;
    std::string name;
};

list<base_struct*> some_list;

struct some_struct : public base_struct {
    double metricA;
};


struct some_other_struct : public base_struct {
    int metricB;
};

base_struct *s1 = new some_struct;
s1->id = 1;
// etc

base_struct *s2 = new some__other_struct;
s2->id = 2;
// etc

some_list.push_back(s1);
some_list.push_back(s2);

棘手的一点是,你必须确保当你获得元素时,你的情况恰如其分。 dynamic_cast可以通过类型安全的方式执行此操作:

some_struct* ss = dynamic_cast<some_struct*>(some_list.front());

您可以使用type_info投标前查询名称:

typeid(*some_list.front()).name();

请注意,这两者都需要使用RTTI进行构建,这通常是正常的,但并不总是因为RTTI具有性能成本并且可能会使内存占用空间膨胀,尤其是在广泛使用模板的情况下。

在之前的项目中,我们使用boost any处理了类似的事情。 any的优点是它允许您混合不是彼此派生的类型。回想起来,我不确定我是否会再次这样做,因为它使代码在运行时有点太容易失败,因为类型检查在此之前被推迟。 (对于dynamic_cast方法也是如此。

在糟糕的旧C日,我们用union

解决了同样的问题
struct base_struct {
    int id;
    std::string name;
    union {  // metricA and metricB share memory and only one is ever valid
        double metricA;
        int metricB;
    };
};

同样,你有一个问题,你必须自己处理确保它是正确的类型。

在STL之前的时代,许多容器系统被编写为采用void*,再次要求用户知道何时投射。理论上,你仍然可以通过说list<void*>来做到这一点,但是你无法查询类型。

编辑:从不,永远使用void*方法!

答案 2 :(得分:0)

我最终使用了一个带有boost :: variant的列表。性能远远优于使用boost :: any。它是这样的:

#include <boost/variant/variant.hpp>
#include <list>

typedef boost::variant< short, int, long, long long, double, string > flex; 
typedef pair<string, flex> flex_pair;
typedef list< flex_pair > row_entry;

list< row_entry > all_records;