当键是对象的一部分时,最合适的关联STL容器

Most appropriate associative STL container when key is part of object [C++]

本文关键字:分时 STL 关联 容器 对象 一部      更新时间:2023-10-16

我有一个这样的类:

struct Thing
{
    unsigned index;
    // more data members
};

我使用一个std::map<Thing>来包含我的Thing s。调用代码看起来像这样:

Thing myThing(/*...*/);
std::map<Thing> things;
things[myThing.index] = myThing;
// ...
Thing &thing3 = things[3];

我想知道是否有一种方法可以直接使用Thing::index,而不需要隐式地将其复制到pair::first

我想我需要提供某种Thing比较运算符,但这没关系。std::set可能工作,但我需要一个完整的Thing对象作为键:

std::set<Thing> things;
Thing &thing3 = *things.find(Thing(3));

除了将Thing重构为std::pair,我可以用STL做得更好吗?

我不明白为什么

inline bool operator<(const Thing& a,const Thing& b) {
  return (a.index<b.index);
}
std::set<Thing> things;  // Uses comparison operator above
Thing &thing3 = *things.find(Thing(3));

并不完全符合您的要求。没有重复/复制索引字段,键比较与映射方法一样高效;有什么不喜欢的呢?

根据下面的注释更新

如果Thing太重量级了,你不想复制它,那么你可能会得到这样的代码:

inline bool operator<(const shared_ptr<Thing>& a,const shared_ptr<Thing>& b) {
  return (a->index < b->index);
}
std::set<shared_ptr<Thing>> things;  // Uses comparison operator above
shared_ptr<Thing> key3(new Thing(3));   
Thing &thing3 = *things.find(key3);

虽然在我看来,重写指针值比较是相当邪恶的,最好是走更详细的路线,显式的"Compare"参数设置模板参数。

有一件事要记住(根据我自己对重量级对象的大优先级队列的经验):与std::pair<trivial_key,heavyweight_object>相比,基于std::pair<trivial_key,shared_ptr<heavyweight_object>>的容器可能有显著的优势,因为前者的遍历只触及键(例如find),与后者相比,可以更高效的缓存,这也会将大量不需要的/不相关的heavyweight_object字节获取到缓存中(当然取决于细节和数字,但实际上这种效果可以很容易地完全淹没复制键的相对较小的成本)。

用私有继承或组合包装映射,重新导出访问器并根据所需的功能实现插入。我建议在key getter上对新类型进行参数化——key getter是一个函数,在给定存储类型的对象时返回一个键。

回想一下,boost::flyweight支持键提取器,以避免在可以从对象中轻松获得键时为键保留额外的存储空间。

我不建议在mapunordered_map足够的所有情况下使用flyweight,我很难相信这些类不支持类似的键提取模式,尽管我在文档或谷歌中找不到任何提及。无论哪种情况,这都可能对您有所帮助

如何使用您的对象的内存地址作为一个键在您的地图?

std::map< void*, std::shared_ptr<MyObject> > myMap;
myMap.insert( std::pair<void*, std::shared_ptr<MyObject> >(object.get(), object) );

所以你不需要在对象中存储键我认为这是一个坏主意因为键与对象状态没有任何关系

相关文章: