C ++ std :: map保存任何类型的值 [英] C++ std::map holding ANY type of value

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

问题描述

基本上,我希望MyClass拥有一个Hashmap,该哈希表将字段名称(字符串)映射到
Value的任何类型。为此,我编写了一个单独的MyField类,其中包含&值信息。.



这是我到目前为止的内容:

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


struct MyClass {
std :: map< string,MyField>领域//错误!!!
}

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



所以我想它必须类似于

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

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




但是显然破坏了我的整个目的,因为声明的地图只能容纳特定类型的MyField。我想要一个可以容纳任何类型的MyField类别的地图。



我能以任何方式实现这一目标..?

解决方案

Blindy的回答非常好(+1),但仅仅是为了完成答案:还有一种不使用库的方法,就是使用动态继承:

  class MyFieldInterface 
{
int m_Size; //当然,请在实际代码中使用适当的访问级别...
〜MyFieldInterface()=默认值;
}

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


struct MyClass {
std :: map< string,MyFieldInterface *>领域
}

优点:




  • 任何C ++编码人员都熟悉

  • 它不会强迫您使用Boost(在某些情况下不允许使用);



缺点:




  • 您必须在堆/免费存储并使用引用语义而不是值语义来操纵它们;

  • 以这种方式公开的公共继承可能导致动态继承的过度使用以及许多与之相关的长期问题

  • 如果必须拥有对象,则指针向量是有问题的,因为您必须管理破坏; / li>


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



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

  struct MyClass { 
std :: map< string,std :: unique_ptr< MyFie ldInterface> >领域//或shared_ptr<>如果您要共享所有权
}

但是仍然存在潜在的问题:



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



但是,如果您需要保持插入对象的顺序,则此技术失去了它的用途。



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






作为对此答案的补充,我想指出一篇非常不错的博客文章,其中总结了您可以使用的所有C ++类型擦除技术,并附带了注释和优点/缺点:




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:

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆