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

c++ container with with variable size and variable types

本文关键字:类型 变量 C++ 容器      更新时间:2023-10-16

我正在尝试创建类似列表的东西。但是,列表的不同实例可能具有不同数量的条目,并且条目类型基于用户给出的输入。例如,用户声明他们希望列表中每个条目的结构包含一个 int id、一个 std::string name、一个双 metricA 和一个长 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 来存储数据,但这会产生比较运算符的问题,并且还会产生比理想情况更多的开销。

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

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

  • 使用映射(std::mapstd::unordered_map(创建"表"而不是结构。将字符串映射到字符串,即将名称映射到值的字符串表示形式,并将它们解释到您的内心。
  • 使用预制变体类型,如 boost::any .
  • 使用多态性 - 将指针存储在列表中,并对值调用虚拟机制调度操作。
  • 为输入语言创建类型系统。然后具有每种类型的值表,并从列表中指向相应的表。

可能还有与程序员C++一样多的其他方法。

有很多方法可以解决具有不同成员的数据结构问题,哪种方法最好在很大程度上取决于它的确切使用方式。

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

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*方法!

我最终使用了一个带有 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;