8

在 C++ 中为每个键创建一个具有任意值类型的关联数组的最佳方法是什么?

目前我的计划是创建一个“值”类,其中包含我期望的类型的成员变量。例如:

class Value {

    int iValue;
    Value(int v) { iValue = v; }

    std::string sValue;
    Value(std::string v) { sValue = v; }

    SomeClass *cValue;
    Value(SomeClass *v) { cValue = c; }

};

std::map<std::string, Value> table;

这样做的一个缺点是您在访问“值”时必须知道类型。IE:

table["something"] = Value(5);
SomeClass *s = table["something"].cValue;  // broken pointer

此外,放入 Value 中的类型越多,数组就越臃肿。

有更好的建议吗?

4

5 回答 5

14

boost::variant似乎正是您正在寻找的。

于 2008-12-29T11:02:15.593 回答
10

你的方法基本上是朝着正确的方向发展的。你必须知道你输入的类型。您可以使用boost::any并且可以将任何内容放入地图中,只要您知道自己放入了什么:

std::map<std::string, boost::any> table;
table["hello"] = 10;
std::cout << boost::any_cast<int>(table["hello"]); // outputs 10

一些答案建议使用boost::variant来解决这个问题。但它不会让您在地图中存储任意类型的值(如您所愿)。您必须事先知道可能的类型集。鉴于此,您可以更轻松地执行上述操作:

typedef boost::variant<int, std::string, void*> variant_type;
std::map<std::string, variant_type> table;
table["hello"] = 10;
// outputs 10. we don't have to know the type last assigned to the variant
// but the variant keeps track of it internally.
std::cout << table["hello"];

这是有效的,因为为此目的boost::variant重载。operator<<重要的是要理解,如果你想保存当前包含在变体中的内容,你仍然必须知道类型,就像下面的boost::any例子一样:

typedef boost::variant<int, std::string, void*> variant_type;
std::map<std::string, variant_type> table;
table["hello"] = "bar";
std::string value = boost::get<std::string>(table["hello"]);

变量的赋值顺序是代码控制流的运行时属性,但任何变量使用的类型都是在编译时确定的。所以如果你想从变量中获取值,你必须知道它的类型。另一种方法是使用访问,如变体文档所述。它之所以起作用,是因为该变体存储了一个代码,该代码告诉它上次分配给它的类型。基于此,它在运行时决定它使用哪个访问者的重载。boost::variant相当大并且不完全符合标准,虽然boost::any符合标准但即使对于小类型也使用动态内存(所以它比较慢。变体可以将堆栈用于小类型)。所以你必须权衡你使用的东西。

如果您确实想将仅在它们做某事的方式上有所不同的对象放入其中,那么多态是一种更好的方法。您可以拥有一个从中派生的基类:

std::map< std::string, boost::shared_ptr<Base> > table;
table["hello"] = boost::shared_ptr<Base>(new Apple(...));
table["hello"]->print();

这基本上需要这个类布局:

class Base {
public:
    virtual ~Base() { }
    // derived classes implement this:
    virtual void print() = 0;
};

class Apple : public Base {
public:
    virtual void print() {
        // print us out.
    }
};

boost::shared_ptr所谓的智能指针。如果您将它们从地图中删除,并且没有其他东西再引用它们,它将自动删除您的对象。理论上,您也可以使用普通指针,但使用智能指针将大大提高安全性。阅读我链接到的 shared_ptr 手册。

于 2008-12-29T10:58:30.813 回答
2

Value使用IntValue,等进行子类化StringValue

于 2008-12-29T10:56:04.623 回答
2

你可以使用联合与 std::map 吗?

Boost::variant 提供无类型变量。

或者,您可以将所有值数据成员设为私有,并提供在未设置时返回错误(或抛出)的访问器。

于 2008-12-29T11:00:49.637 回答
1

一个直接的优化是使用 a union,因为你总是只有一个值作为键。

更完整的解决方案是将一些运行时类型信息封装到接口中。主要是“这是哪种类型?” 和“我如何比较平等的价值观?” 然后使用它的实现作为关键。

于 2008-12-29T11:01:13.880 回答