C++ 具有任意类型值的关联数组
在 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 中放入的类型越多,数组就会变得越臃肿。
还有更好的建议吗?
What is the best way to have an associative array with arbitrary value types for each key in C++?
Currently my plan is to create a "value" class with member variables of the types I will be expecting. For example:
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;
A downside with this is you have to know the type when accessing the "Value". i.e.:
table["something"] = Value(5);
SomeClass *s = table["something"].cValue; // broken pointer
Also the more types that are put in Value, the more bloated the array will be.
Any better suggestions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
boost::variant 似乎正是您正在寻找的。
boost::variant seems exactly what you are looking for.
你的方法基本上是朝着正确的方向。 您将必须知道您输入的类型。 您可以使用
boost::any
并且您可以将几乎任何内容放入地图中,只要您知道放入的内容:一些答案建议使用
boost::变体
来解决这个问题。 但它不会让您在地图中存储任意类型的值(就像您想要的那样)。 您必须事先知道可能的类型集。 鉴于此,您可以更轻松地执行上述操作:这是有效的,因为
boost::variant
为此目的重载了operator<<
。 重要的是要理解,如果您想保存变体中当前包含的内容,您仍然必须知道类型,就像在boost::any
情况中一样:变体的赋值顺序是代码控制流的运行时属性,但任何变量使用的类型是在编译时确定的。 因此,如果你想从变体中获取值,你必须知道它的类型。 另一种方法是使用访问,如变体文档所述。 它之所以有效,是因为变体存储了一个代码,告诉它最后分配给它的是哪种类型。 基于此,它在运行时决定使用哪个访问者重载。
boost::variant
相当大,并且不完全符合标准,而boost::any
符合标准,但即使对于小型类型也使用动态内存(因此速度较慢。variant可以将堆栈用于小型类型)。 所以你必须权衡你所使用的东西。如果您确实想将对象放入其中,而这些对象仅在执行某些操作的方式上有所不同,那么多态性是更好的方法。 您可以拥有一个从中派生的基类:
这基本上需要此类布局:
boost::shared_ptr
是所谓的智能指针。 如果您将对象从地图中删除并且没有其他对象再引用它们,它将自动删除它们。 理论上,您也可以使用普通指针,但使用智能指针将大大提高安全性。 阅读我链接到的shared_ptr 手册。Your approach was basically into the right direction. You will have to know the type you put into. You can use
boost::any
and you will be able to put just about anything into the map, as long as you know what you put into:Some answers recommended the use of
boost::variant
to solve this problem. But it won't let you store arbitrary typed values in the map (like you wanted). You have to know the set of possible types before-hand. Given that, you can do the above more easily:That works because
boost::variant
overloadsoperator<<
for that purpose. It's important to understand that if you want to save what is currently contained in the variant, you still have to know the type, as with in theboost::any
case:The order of assignments to a variant is a runtime property of the control flow of your code, but the type used of any variable is determined at compile time. So if you want to get the value out of the variant, you have to know its type. An alternative is to use visitation, as outlined by the variant documentation. It works because the variant stores a code which tells it which type was last assigned to it. Based on that, it decides at runtime which overload of the visitor it uses.
boost::variant
is quite big and is not completely standard compliant, whileboost::any
is standard compliant but uses dynamic memory even for small types (so it's slower. variant can use the stack for small types). So you have to trade off what you use.If you actually want to put objects into it which differ only in the way they do something, polymorphism is a better way to go. You can have a base-class which you derive from:
Which would basically require this class layout:
The
boost::shared_ptr
is a so-called smart pointer. It will delete your objects automatically if you remove them out of your map and nothing else is referencing them them anymore. In theory you could have worked with a plain pointer too, but using a smart pointer will greatly increase safety. Read the shared_ptr manual i linked to.使用
IntValue
、StringValue
等对Value
进行子类化。Subclass
Value
withIntValue
,StringValue
, and so on.您可以使用 std::map 的联合吗?
Boost::variant 提供无类型变量。
或者,您可以将所有值数据成员设为私有,并提供在未设置时返回错误(或抛出)的访问器。
Can you use a union with std::map?
Boost::variant provides typeless variables.
Altrnatively you could make all your Value data members private and provide accessors that return an error (or throw) if it isn't set.
一种直接的优化是使用
union
,因为您始终只有一个值作为键。更完整的解决方案是将一些运行时类型信息封装到接口中。 主要是“这是什么类型?” 和“如何比较价值观是否平等?” 然后使用它的实现作为关键。
A straight-forward optimisation would be to use a
union
, since you'll always only have one of the values as key.A more complete solution would encapsulate some run time type information into a interface. Primarily "Which type is this?" and "How do I compare values for equality?" Then use implementations of that as key.