0

我正在尝试创建类似列表的内容。但是,列表的不同实例可能具有不同数量的条目,并且条目的类型基于用户给出的输入。例如,用户声明他们希望列表中每个条目的结构包含一个 int id、一个 std::string 名称、一个双精度指标 A 和一个长指标 B。基于此输入,创建以下内容:

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 来存储数据,但这会给比较运算符带来问题,并且会产生比理想情况更多的开销。

4

3 回答 3

4

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

当然,现在有一些方法可以在 C++ 中处理此类问题。仅举几例:

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

可能有与 C++ 程序员一样多的其他方法可以做到这一点。

于 2012-05-12T19:33:13.723 回答
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 时代,我们用 a 解决了同样的问题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*方法!

于 2012-05-12T19:37:10.340 回答
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;
于 2012-05-13T22:35:41.073 回答