Let's say I have the following:
struct MetadataThingy {
void *actual_thingy;
int some_metadata;
int more_metadata;
bool operator<(MetadataThingy const& other) const {
return actual_thingy < other.actual_thingy;
}
};
where actual_thingy
points to some data of importance and I want the container ordered by the value of actual_thingy
rather than the value of the element pointed at, but I need to store some other data about it, so I created the wrapper class MetadataThingy
with a comparator that only considers the value of the actual_thingy
pointer (rather than using a container of void *
)
Now, given the following code:
std::set<MetadataThingy> thingy_set;
void test() {
MetadataThingy m1 { nullptr, 5, 20 };
MetadataThingy m2 { &m1, 1, 2 };
MetadataThingy m3 { &m2, 6, 0 };
thingy_set.insert(m1);
thingy_set.insert(m2);
thingy_set.insert(m3);
MetadataThingy m;
m = *thingy_set.find(m2); // OK.
m = *thingy_set.find(static_cast<void *>(&m2)); // Nope. Can't use a pointer.
}
Since each MetadataThingy
can be uniquely identified by the pointer value it stores and is ordered by the pointer value, it would make sense to find/delete objects simply by using a void *
as the key. As it currently stands, though, I would have to create a dummy MetadataThingy
each time I search for an element, which feels really kludgy. I've already considered using just a map
with pointers as key and MetadataThingy
as value but since each MetadataThingy
must also contain the pointer anyway, this feels a bit redundant. So, is there a way to use an element of a type other than that stored in a set to find or delete values in the set, given that elements of the two types are mutually comparable and that elements of one type can be uniquely mapped into the other ( void *
and MetadataThingy
are isomorphic)? (I didn't include any in the above code, but suppose there are operator overloads for comparing void *
and MetadataThingy
in any order.)
A little background on the problem I'm trying to solve, just in case anyone can recommend a better approach: I need to order a collection by multiple criteria, so I have several MetadataThingy
containers, all sorted by different criteria. "Metadata" in this case would be stuff I need to track the positions of the elements in all containers so that I can do fast removal. This would sound like a perfect job for boost multi-index containers, but the ordering of these elements is constantly changing, which AFAIK would mean it won't work.
As of C++14, std::set
has templated versions of its lookup functions find
, lower_bound
, etc. They allow you to pass any object for comparison, as long as the comparer supports it.
This means you can directly pass your void*
to find
, as long as the comparer supports comparing MetadataThingy
and void*
.
For more information, see http://en.cppreference.com/w/cpp/container/set/find.
To understand the limitation regarding Compare::is_transparent
, I found this StackOverflow question very helpful.