I am having issues understanding how std::set (or std::map etc) identify unique keys. The thing I am trying to do is wrap a struct object within boost::shared_ptr and then store that shared pointer within std::set container:
Let’s say the struct is a color:
struct Color {
float r;
float g;
float b;
};
Then the container and comparison function object is defined in another class:
class AnotherClass {
typedef boost::shared_ptr<Color> ColorPtr;
public:
struct ColorCompare {
bool operator()(const ColorPtr &a, const ColorPtr &b) const {
return (a->r > b->r) && (a->g > b->g) && (a->b > b->b);
}
};
private:
// Container definition
std::set<ColorPtr, ColorCompare> colors;
};
The code above is failing to uniquely identify shared_ptr objects based on their wrapped Color struct. I always thought that the std::set container would run a comparison function on two objects, and if none of them is greater than or less than the other – it will assume they are equal. Note, that I cannot use default shared_ptr::operator<() and less<...> as that implementation is based on pointer address.
What am I missing?
p.s. I am wrapping colors within shared_ptr since I need to know their reference count at some point (and remove colors that are at ref count 1 – that is, only referenced by std::set container itself). Is there a better way to get the same result?
The comparison needs to be a strict weak ordering, which yours isn’t. (For example, how are (1,0,0) and (0,1,0) ordered?) Do this:
This is the standard lexicographic ordering on a tuple of elements.
Note that you would write
!(b->r > a->r)instead ofa->r == b->rin general so that you don’t introduce a dependence on a compatible equality operator, though in this simple case of floats we’re fine.By the way, you don’t need a
struct, you can simply declare astatic bool ColorCompare(...);function. Another option is to define anoperator<directly in thestruct Colorto make everything self-contained (so you’ll just need a generic dereferencing-comparator for (smart) pointers).