当前位置: 首页 > 知识库问答 >
问题:

C++std::Map保存任何类型的值

卜高超
2023-03-14

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

这是我目前所掌握的:

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


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

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

所以我想应该是

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

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

我有什么办法可以做到这一点吗?

共有1个答案

皮骏
2023-03-14

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

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

优点:

  • 它对任何C++编码器都很熟悉
  • 它不会强制您使用Boost(在某些上下文中不允许使用);

缺点:

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

因此,如果可以,请使用boost::any或boost::variant作为默认值,只有在其他情况下才考虑此选项。

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

然而,还有一个潜在的问题:

它强制您使用new/delete(或make_unique/shared)创建对象。这意味着实际的对象是在空闲存储区(堆)中由分配器提供的任何位置(主要是默认位置)创建的。因此,由于缓存丢失,查看对象列表的速度往往不如可能的快。

如果您关心尽可能快地循环此列表的性能(如果不关心,则忽略以下内容),那么您最好使用boost::variant(如果您已经知道将要使用的所有具体类型)或使用某种类型擦除的多态容器

下面是一个例子(图片来自那里):http://bannalia.blogspot.fr/2014/05/fast-polymorphic-collections.html

但是,如果您需要保持插入对象的顺序,那么这种技术就不受欢迎了。

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

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

  • http://talesofcpp.fusionfenix.com/post-16/eption-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应用程序中访问这些数据。我还需要能够通过可变键名关联地访问项目;例如: A<code>std::vector 我有办法做到这一点吗?我确实更喜欢使用向量和地图,但我对其他解决方案持开放态度,只要我可以关联地访问数据项。 使用这里描述的模板是可行的方法吗?

  • 以下代码片段适用于Visual Studio 2008,但不适用于Visual Studio 2010。 用法 我得到以下错误: 1个 如果用typedef int MyValueType替换typedef STD::tr1::tuple myvalue type,则有效。 提前感谢您。

  • 问题内容: 是否有与C ++等效的Java Map keySet()? Java 方法返回“此映射中包含的键的设置视图”。 问题答案: 也许以下可能有用: 使用STL兼容序列(例如std :: vector,std :: deque或std :: list)的 make_key_set 函数的重载可以如下所示:

  • 问题内容: 我正在寻找具有C ++ std :: map的通常实现特征的Java类(据我了解,这是一个自平衡二进制搜索树): 插入/删除/搜索的O(log n)性能 每个元素均由唯一键和映射值组成 按键遵循严格的弱顺序 我正在寻找带有开源或设计文档的实现;我可能最终会放弃对原始键/值的支持。 这个问题的风格类似于:std :: deque的Java等效 ,其答案是“ Java Primitive

  • 问题内容: 是否可以在条件表达式包含任意类类型的对象的GDB中设置条件断点? 我需要在函数内部设置一个断点,条件将检查条件是否是对象的成员字符串变量等于“ foo”。因此,类似: 但这不起作用。GDB是否只允许在基本类型和char *类型上使用条件断点?有什么办法可以在非原始类型上设置条件断点? 问题答案: 有什么办法可以在非原始类型上设置条件断点? 是的,一种实现方法是将非原始类型转换为原始类型

  • 我目前在C#中面临一个问题,我认为可以使用存在类型来解决这个问题。然而,我真的不知道它们是否可以在C#中创建,或者模拟(使用其他构造)。