Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a class like this:

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

I am using a std::map<Thing> to contain my Things. Calling code looks something like this:

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

I was wondering if there was an approach that could use Thing::index directly without the need implicitly to copy it into pair::first.

I guess I would need to provide some sort of Thing comparison operator, but that's OK. std::set might work, but I would need a whole Thing object as key:

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

Other than refactoring Thing into a std::pair, can I do better with STL?

like image 691
paperjam Avatar asked Oct 09 '22 16:10

paperjam


1 Answers

I don't see why

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

doesn't do exactly what you want. No duplication/copying of the index field, and key comparison as efficient as the map approach; what's not to like ?

Update in light of comments below:

If Thing is so heavyweight that you don't want to copy it, then you'd probably end up with code more like:

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

although IMHO overriding pointer value comparison is fairly evil and it'd be better to go the more verbose route of an explicit "Compare" argument to the set template args.

One thing to bear in mind (from my own experience of big priority queues of heavyweight objects): there may be significant advantages to std::pair<trivial_key,shared_ptr<heavyweight_object>>-based containers over std::pair<trivial_key,heavyweight_object> due to the fact that traversals of the former touching only keys (e.g find) can be much more cache efficient compared with the latter which will also fetch a lot of mostly unneeded/irrelevant heavyweight_object bytes into cache (depends on the details and numbers of course, but in fact this effect could easily completely swamp the relatively minor cost of duplicating the key).

like image 93
timday Avatar answered Oct 14 '22 02:10

timday