C++ std::map 持有任何类型的值

C++ std::map holding ANY type of value(C++ std::map 持有任何类型的值)

本文介绍了C++ std::map 持有任何类型的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上我想要 MyClass 持有一个 Hashmap 将字段名称(字符串)映射到任何类型的值.. 为此,我编写了一个单独的 MyField 类来保存类型 &价值信息..

这是我目前所拥有的:

template 类 MyField {T m_Value;int m_Size;}结构我的类{std::map领域;//错误!!!}

但是正如您所看到的,地图声明失败了,因为我没有为 MyField 提供类型参数...

所以我想它必须是这样的

std::map<字符串,MyField>领域;

std::map<字符串,MyField>领域;


但显然这破坏了我的整个目的,因为声明的地图只能保存特定类型的 MyField ..我想要一个可以保存任何类型的 MyField 类的地图..

有什么办法可以实现这个目标吗?

解决方案

Blindy 的回答很好(+1),但只是为了完成答案:还有另一种方法可以在没有库的情况下使用动态继承:

class MyFieldInterface{int m_Size;//当然在实际代码中使用适当的访问级别...~MyFieldInterface() = 默认值;}模板 类 MyField :公共 MyFieldInterface {T m_Value;}结构我的类{std::map领域;}

优点:

  • 任何 C++ 编码人员都熟悉它
  • 它不会强迫您使用 Boost(在某些情况下您不允许使用);

缺点:

  • 你必须在堆/空闲存储上分配对象并使用引用语义而不是值语义来操作它们;
  • 以这种方式公开的公共继承可能会导致过度使用动态继承以及许多与类型相关的长期问题,因为您的类型确实过于相互依赖;
  • 如果指针向量必须拥有对象,则它是有问题的,因为您必须管理销毁;

因此,如果可以,请使用 boost::any 或 boost::variant 作为默认值,否则仅考虑使用此选项.

要解决最后一个缺点,您可以使用智能指针:

struct MyClass {std::map>领域;//或 shared_ptr<>如果您共享所有权}

但是还有一个潜在的问题:

它强制您使用 new/delete(或 make_unique/shared)创建对象.这意味着实际对象是在分配器提供的任何位置(主要是默认位置)的空闲存储(堆)中创建的.因此,由于

然而,如果您需要保持对象插入的顺序,这种技术就会失去兴趣.

无论如何,有几种可能的解决方案,这在很大程度上取决于您的需求.如果您对自己的案例没有足够的经验,我建议您使用我在示例中首先解释的简单解决方案或 boost::any/variant.

<小时>

作为对这个答案的补充,我想指出非常好的博客文章,这些文章总结了您可以使用的所有 C++ 类型擦除技术,并附有评论和优缺点:

  • http://talesofcpp.fusionfenix.com/post-16/episode-9-erasing-the-concrete
  • http://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/
  • http://akrzemi1.wordpress.com/2013/12/06/type-erasure-part-ii/
  • http://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/
  • http://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/

Basically I want MyClass that holds a Hashmap that maps Field name(string) to ANY type of Value.. For this purpose I wrote a separate MyField class that holds the type & value information..

This is what I have so far:

template <typename T>
class MyField {
    T m_Value;
    int m_Size;
}


struct MyClass {
    std::map<string, MyField> fields;   //ERROR!!!
}

But as you can see, the map declaration fails because I didn't provide the type parameter for MyField...

So I guess It has to be something like

std::map< string, MyField<int> > fields;

or

std::map< string, MyField<double> > fields;


But obviously this undermines my whole purpose, because the declared map can only hold MyField of a specific type.. I want a map that can hold ANY type of MyField clas..

Is there any way I can achieve this..?

解决方案

Blindy's answer is very good (+1), but just to complete the answer: there is another way to do it with no library, by using dynamic inheritance:

class MyFieldInterface
{
    int m_Size; // of course use appropriate access level in the real code...
    ~MyFieldInterface() = default;
}

template <typename T>
class MyField : public MyFieldInterface {
    T m_Value; 
}


struct MyClass {
    std::map<string, MyFieldInterface* > fields;  
}

Pros:

  • it's familiar to any C++ coder
  • it don't force you to use Boost (in some contexts you are not allowed to);

Cons:

  • you have to allocate the objects on the heap/free store and use reference semantic instead of value semantic to manipulate them;
  • public inheritance exposed that way might lead to over-use of dynamic inheritance and a lot of long-term issues related to your types really being too inter-dependent;
  • a vector of pointers is problematic if it have to own the objects, as you have to manage destruction;

So use boost::any or boost::variant as default if you can, and consider this option only otherwise.

To fix that last cons point you could use smart pointers:

struct MyClass {
    std::map<string, std::unique_ptr<MyFieldInterface> > fields;  // or shared_ptr<> if you are sharing ownership
}

However there is still a potentially more problematic point:

It forces you to create the objects using new/delete (or make_unique/shared). This mean that the actual objects are created in the free store (the heap) at any location provided by the allocator (mostly the default one). Therefore, going though the list of objects very often is not as fast as it could be because of cache misses.

If you are concerned with performance of looping through this list very often as fast as possible (ignore the following if not), then you'd better use either boost::variant (if you already know all the concrete types you will use) OR use some kind of type-erased polymorphic container.

The idea is that the container would manage arrays of objects of the same type, but that still expose the same interface. That interface can be either a concept (using duck-typing techniques) or a dynamic interface (a base class like in my first example). The advantage is that the container will keep same-type objects in separate vectors, so going through them is fast. Only going from one type to another is not.

Here is an example (the images are from there): http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html

However, this technique loose it's interest if you need to keep the order in which the objects are inserted.

In any way, there are several solutions possible, which depends a lot on your needs. If you have not enough experience with your case, I suggest using either the simple solution I first explained in my example or boost::any/variant.


As a complement to this answer, I want to point very good blog articles which summarize all C++ type-erasure techniques you could use, with comments and pros/cons:

  • http://talesofcpp.fusionfenix.com/post-16/episode-nine-erasing-the-concrete
  • http://akrzemi1.wordpress.com/2013/11/18/type-erasure-part-i/
  • http://akrzemi1.wordpress.com/2013/12/06/type-erasure-part-ii/
  • http://akrzemi1.wordpress.com/2013/12/11/type-erasure-part-iii/
  • http://akrzemi1.wordpress.com/2014/01/13/type-erasure-part-iv/

这篇关于C++ std::map 持有任何类型的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:C++ std::map 持有任何类型的值

基础教程推荐