具有值的任意类型的C ++关联数组

时间:2008-12-29 10:52:23

标签: c++ arrays stl boost

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

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

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

此外,放入Value的类型越多,阵列就会越膨胀。

有更好的建议吗?

5 个答案:

答案 0 :(得分:14)

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

答案 1 :(得分:9)

你的方法基本上是正确的方向。您必须知道您输入的类型。您可以使用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符合标准,但即使对于小型也使用动态内存(因此速度较慢.variant可以将堆栈用于小型)。所以你必须权衡你使用的东西。

如果你真的想把对象放入其中,而这些对象只是在他们做某事的方式上有所不同,那么多态性是一种更好的方法。您可以拥有一个基类:

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手册。

答案 2 :(得分:2)

包含ValueIntValue的子类StringValue,等等。

答案 3 :(得分:2)

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

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

另外,您可以将所有的Value数据成员设为私有,并提供返回错误(或抛出)的访问器(如果未设置)。

答案 4 :(得分:1)

直接优化将使用union,因为您始终只将其中一个值作为键。

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