Reputation: 8508
I have a class like this:
struct Thing
{
unsigned index;
// more data members
};
I am using a std::map<Thing>
to contain my Thing
s. 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?
Upvotes: 5
Views: 291
Reputation: 24892
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).
Upvotes: 2
Reputation: 26943
I Recall that boost::flyweight supports key extractors to avoid keeping extra storage for the key, when it is readily obtainable from the object.
I'm not suggesting to use flyweight in all cases where map
or unordered_map
are sufficient, I have a hard time believing those classes won't support a similar key extraction pattern, although i can't find any mention in the docs or google. In either case this might be helpful to you
Upvotes: 1
Reputation: 23916
Wrap map with private inheritance or composition, reexport accessors and implement insertion in terms of required functionality. I'd recommend to patametrize your new type on key getter - a functor that returns a key given an object of storage type.
Upvotes: 1
Reputation: 4164
What about using the memory address of your object as a key in your map??
std::map< void*, std::shared_ptr<MyObject> > myMap;
myMap.insert( std::pair<void*, std::shared_ptr<MyObject> >(object.get(), object) );
So you don't need to store the key in your object which I think is a bad idea because the key does not has any relation with the objects state.
Upvotes: 0