C++ std::map key sort comparison function? - c++

I have a class where I overload all the comparison operators. I load a bunch of these objects into a map as keys and I expect the map to sort them least to greatest.
However, the map is actually sorted by greatest to least. Why is this? Does it not use the comparison operators on the key when it sorts the map? How can I make it do this?

If you look at the definition for std::map, the 3rd parameter is the comparator - std::less< Key > by default. The default implementation of which is just to invoke operator<.
You could try defining a new version in the header where YourType is defined, like so:
template<> std::less< YourType >
{
bool operator()(
const YourType& lhs,
const YourType& rhs)
{
if(!(lhs.member1 < rhs.member1))
{
return false;
}
if(!(lhs.member2 < rhs.member2))
{
return false;
}
return true;
}
}
Remember to friend std::less< YourType > in your class.
The actual test must comply with the following or you'll get all kinds of problems:
lhs < rhs = true
rha < lhs = false
lhs < lhs = false
One important thing to note (that I discovered quite recently) is that if your compiler starts complaining that you've redefined the meaning of std::less or other funky things, it could be that you've only forward declared your YourType when you declared your std::map< YourType >.
Hope this helps!

You should post your code that implements the overload of the comparison operators, so may answer is based on guessing:
Your problem could be a simple typo (I did that once)
bool operator < ( T lhs, T rhs ) const
{
return rhs < lhs; // note: lhs is on the right!
}
but actually you want:
bool operator < ( T lhs, T rhs ) const
{
return lhs < rhs;
}

Related

Comparsion between structs for a set

This code works but having a struct called ptrcomp outside the weighted_pointer seems (to me) that they are different things. I tried some different ways and even googled it, but I haven't found anything that works like this.
struct node{
unsigned int oper;
void * a;
void * b;
};
struct weighted_pointer{
mutable int weight;
unique_ptr<node> pointer;
};
struct ptrcomp{
bool operator()(const weighted_pointer & lhs, const weighted_pointer & rhs) {
return tie(lhs.pointer->oper, lhs.pointer->a, lhs.pointer->b) < tie(rhs.pointer->oper, rhs.pointer->a, rhs.pointer->b);
}
};
set<weighted_pointer,ptrcomp> gate;
My objective is to make the std::set working. And possibly write it like set<weighted_pointer>.
having a struct called ptrcomp outside the weighted_pointer seems (to me) that they are different things.
That's how things really are. weighted_pointer is data, while ptrcomp is a way to compare the data. So, these two really are different things, and there is nothing wrong with your code.
If it happens that you have one canonical way of comparing your data, make it into operator <:
bool operator < (const weighted_pointer & lhs, const weighted_pointer & rhs) {
return tie(lhs.pointer->oper, lhs.pointer->a, lhs.pointer->b) < tie(rhs.pointer->oper, rhs.pointer->a, rhs.pointer->b);
}
std::set will happily use it, if you use it as std::set<weighted_pointer> (in fact, std::set has the second template parameter defaulted to std::less<T>, which is a comparator class that uses operator <).
If you change your code to
struct weighted_pointer {
mutable int weight;
unique_ptr<node> pointer;
bool operator < (const weighted_pointer & rhs) const;
};
bool weighted_pointer::operator < (const weighted_pointer & rhs) const {
return tie(pointer->oper, pointer->a, pointer->b) < tie(rhs.pointer->oper, rhs.pointer->a, rhs.pointer->b);
}
then it will work and you won't need a comparator ptrcomp for the set and can use the type set<weighted_pointer> as you wished. (You can also move the definition into the struct if you wish.)
struct weighted_pointer {
// ...
struct compare {
// ...
};
};
set<weighted_pointer,weighted_pointer::compare> gate;
// better
using weighted_pointer_set = set<weighted_pointer,weighted_pointer::compare>;
weighted_pointer_set gate;
This is how I see this usually done.
Having a std::set<weighted_pointer> means that the set uses std::less to compare the elements. This in turn calls operator< on the respective type, so if you provide an implementation of that operator it'll work.

'operator<' error while creating an std::pair<A,B>

I already have a workaround for this but would still like to understand the problem.
I have a Multimap
multimap<QPoint,Figure*> mymap;
QPoint is a class from Qt 5.4. Figure* is a pointer to a
class I have concocted myself.
Now I want to add elements to that map.
This sample works fine:
multimap<int,int> test;
test.insert(pair<int,int>(41,43));
As does this one (being said workaround)
std::pair<QPoint,Figure*> p;
p.first = pos;
p.second = sub_fig;
mymap.insert(p);
However, the plain first reflex of
std::pair<QPoint,Figure*> p(pos, sub_fig);
has the compiler at that line state something like:
[..]
scanner.cpp:264:17: required from here
/usr/include/c++/4.9/bits/stl_function.h:371:20: error: no match for
‘operator<’ (operand types are ‘const QPoint’ and ‘const QPoint’)
{ return __x < __y; }
[..]
followed by the usual five kilometers of stacked STL error messages.
First: The 'types' are not QPoint and QPoint. They are, as
stated above, QPoint and Figure*.
Anyone who can riddle this?
CORRECTION
My work-around does not work either after all. I had forgotten to
de-comment
res.insert(p);
Here is the complete pertinent code:
multimap<QPoint,Figure*> res;
// ...
vector<Figure*> stack = figure->get_above_figure_stack();
for (vector<Figure*>::const_iterator CI2=stack.begin();
CI2!=stack.end();CI2++)
{
// ..
Figure* sub_fig = *CI2;
std::pair<QPoint,Figure*> p;
p.first = pos;
p.second = sub_fig;
res.insert(p); // <- The bad line.
}
multimap needs an ordering relation for the keys, and its default is to use < (in the guise of std::less).
Since QPoint doesn't have an overload of operator<, the compiler is complaining that it doesn't exist.
It's not difficult to provide one:
bool operator< (const QPoint& lhs, const QPoint& rhs)
{
return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y());
}
or
bool lessQPoints (const QPoint& lhs, const QPoint& rhs)
{
return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y());
}
multimap<QPoint, Figure*, lessQPoints> mymap;
The keys in a multimap are ordered by default with std::less which invokes operator< on the key type.
Your key object (QPoint) has no operator< to do the comparison.
You will need to provide your own comparison function using the approprate multimap constructor.

Find equal instance in a c++ set

I'm using a c++ STL set and I want to know if it's present in the set an equivalent instance. To retrive the instance I'm using the find set method. The problem is that it doesn't work. I think the problem is in my comparator object:
bool SetComparator::operator ()( const Point* i1, const Point* i2 ) const {
if ( *i1 == *i2 )
return false;
return true;
}
The operator == is redefined for the class Point in a simple way:
bool Point::operator ==( const Point& p ) const {
if (x == p.x && y == p.y)
return true;
return false;
}
After a debugging I can see that the find method calls operator() but it doesn't find the same instance so the find returns end() but I know that there is an equal object. I think the problem is related to the set internal order. How can I do?
std::set uses partial ordering (i.e. the operator<), so when you pass in an operator that can only decide equality, you break the assumption of the implementation of std::set. Your SetComparator has to behave similar to std::less.
For example std::pair (utility) implements relational operators for two items, e.g. for operator<:
template <class T1, class T2>
bool operator< (const std::pair<T1,T2>& lhs, const std::pair<T1,T2>& rhs) {
return lhs.first<rhs.first || (!(rhs.first<lhs.first) && lhs.second<rhs.second);
}
note that (!(rhs.first<lhs.first) && lhs.second<rhs.second) is a workaround for (rhs.first == lhs.first && lhs.second < rhs.second) using only operator<
If you only want to check for equality maybe using std::set is the wrong decision. If you can hash your objects, you could use a std::unordered_set (C++11 and later).

Creating a composite type from two enum classes, ready for STL map

I would like to create a composite type out of two enum classes.
enum class Color {RED, GREEN, BLUE};
enum class Shape {SQUARE, CIRCLE, TRIANGLE};
class Object {
Color color;
Shape shape;
public:
};
In order to use Object in an STL container like std::map<> I would need to overload the less-than operator. However, in order to flatten both enum classes into one linear index I somehow need the number of elements (NoE) of the enum classes:
friend bool operator< (const Object &lhs, const Object &rhs) {
return NoE(Shape)*lhs.color+lhs.shape < NoE(Shape)*rhs.color+rhs.shape;
}
How can this be done without entering the same information (number of elements) in two places in the program in a nice way? (Nice way means no FIRST_ELEMENT, LAST_ELEMENT, preprocessor magic, etc.)
Question (Number of elements in an enum) is similar but does not address enum classes.
I would like to know what is the best way to implement this kind of composite types in C++11. Is the enum class definition strong enough, or is it necessary to say:?
enum class Color {RED=0, GREEN=1, BLUE=2};
enum class Shape {SQUARE=0, CIRCLE=1, TRIANGLE=2};
As commented and as already stated by others, give precedence to either Shape or Color in the operator< and only compare the other if the first is equal.
An alternative implementation for operator< using std::tie:
#include <tuple>
friend bool operator<(const Object& lhs, const Object& rhs)
{
return std::tie(lhs.color, lhs.shape) < std::tie(rhs.color, rhs.shape);
}
Consider using simply std::tuple<Color, Shape> as the "composite enum." This will come with comparison operators already defined for you, using a dictionary ordering. For example, valid code:
bool b = std::make_tuple(Color::RED, Shape::CIRCLE)
< std::make_tuple(Color::GREEN, Shape::SQUARE);
You don't need a linear index, you can simply compare them lexicographically:
friend bool operator< (const Object &lhs, const Object &rhs) {
if (lhs.color < rhs.color) return true;
else if (lhs.color > rhs.color) return false;
else return lhs.shape < rhs.shape;
}
That's a good question, but you don't actually need the number of Color to compare them :
friend bool operator< (const Object &lhs, const Object &rhs) {
if(lhs.color > rhs.color) {
return false;
}
if(lhs.color < rhs.color) {
return true;
}
return lhs.shape < rhs.shape;
}
You only need to compare shape if color is the same for both.
Using a ternary you can make it look nice too:
friend bool operator< (const Object &lhs, const Object &rhs) {
return lhs.color == rhs.color ? (lhs.shape < rhs.shape)
: (lhs.color < rhs.color);
}
What you are trying to express is that to determine the order of your Objects, you first need to compare the color, and then check the shape in case the color was the same. Instead of linearizing that, if would simply use boolean operators.
friend bool operator< (const Object &lhs, const Object &rhs)
{
return ( (lhs.color < rhs.color)
|| ( (lhs.color == rhs.color ) && ( lhs.shape < rhs.color) ) )
}
EDIT: Actually, you can also use an upper bound for the number of objects, the behaviour will be the same:
friend bool operator< (const Object &lhs, const Object &rhs) {
return 10000*lhs.color+lhs.shape < 10000*rhs.color+rhs.shape;
}
but that introduces a "magic number" (so not such a good idea).

How should I compare pairs of pointers (for sort predicate)

I have a STL container full of billions of the following objects
pair<SomeClass*, SomeClass*>
I need some function of the following form
/*returns items sorted biggest first */
bool sortPredicate (pair<SomeClass*, SomeClass*>two, pair<SomeClass*, SomeClass*> one)
{
return ???;
}
Is there some trick I can use to very quickly compare pairs of pointers?
Edit 1: A clarification
In the end I just want to sort the list of pointer-pairs such that all of the duplicates are next to each other. Assume that there is no clear method in SomeClass that can be used for this purpose---I only have pointer pairs, and I want to find all identical pairs (in parallel). I thought a sort would do the trick, but if you can think of a better parallel method, let me know.
Edit 2: A clarification
Fixed my code (the arguments to the sort predicate were wrong--they should be pairs).
It is a quirk of C++ that arbitrary pointers of the same type are not (necessarily) comparable with <, but are comparable with std::less.
Unfortunately, the operator< for std::pair is defined in terms of operator< on the components, not std::less.
So, assuming that you want two pairs to fall in the same sort position if and only if they point to the same two objects, you need:
// "less than"
template<typename T>
bool lt(const T &lhs, const T &rhs) {
return std::less<T>()(lhs, rhs);
}
typedef std::pair<SomeClass*, SomeClass*> mypair;
bool sortPredicate(const mypair &lhs, const mypair &rhs) {
return lt(lhs.first, rhs.first)
|| (!lt(rhs.first, lhs.first) && lt(lhs.second, rhs.second));
}
On pretty much any system you can name, this should compile to the same code as return lhs < rhs;, but that is not formally correct. If the referands of the pointers are all subobjects of the same object (for instance if you have a huge array and all the pairs point to elements of that one array), then operator< is OK for the pointers and hence OK for std::pair<pointer,pointer>.
If you want to pairs to fall in the same sort position if and only if the objects they point to sort the same, then you'd add the extra dereference:
bool sortPredicate(const mypair &lhs, const mypair &rhs) {
return lt(*lhs.first, *rhs.first)
|| (!lt(*rhs.first, *lhs.first) && lt(*lhs.second, *rhs.second));
}
and perhaps you'd also add checks for null pointers, if those are permitted. Of course if you know that SomeClass really is a class type, not a pointer type, then you don't need to use std::less in the version above, just define operator< for SomeClass and:
inline bool lessptr(const SomeClass *lhs, const SomeClass *rhs) {
if (lhs == 0) return rhs != 0;
if (rhs == 0) return false;
return *lhs < *rhs;
}
bool sortPredicate(const mypair &lhs, const mypair &rhs) {
return lessptr(lhs.first, rhs.first)
|| (!lessptr(rhs.first, lhs.first) && lessptr(lhs.second, rhs.second));
}
You may or may not be able to optimise that a bit, since there are some repeated null checks performed in both the first and second calls to lessptr. If you care that much, see what the compiler does with it.
Assuming your class has comparison operators:
bool sortPredicate (SomeClass *two, SomeClass *one)
{
return *two > *one;
}
If you just want to compare the pointer addresses, use std::greater<T>:
sort(container.begin(), container.end(), std::greater<SomeClass *>());
EDIT: OK, I really have no idea what you are trying to do now, with your most recent edit. Why not just use the default sort, if all you want to do is find duplicates?
If I understand correctly Your predicate should have the following signature
bool sortPredicate(pair<SomeClass*, SomeClass*>& lhs, pair<SomeClass*, SomeClass*>& rhs);
I know nothing about Your class and if there is any natural order for it, so it's hard to guess how You want to sort it. In The comment You write that the biggest items should be first. I assume there is < operator for the class. How about this?
bool sortPredicate(pair<SomeClass*, SomeClass*>& lhs, pair<SomeClass*, SomeClass*>& rhs)
{
if(!(*(lhs.first) < *(rhs.first) || *(rhs.first) < *(lhs.first))) // If there is == operator use it.
{
return *(rhs.second) < *(lhs.second);
}
else
{
return *(rhs.first) < *(lhs.first);
}
}
EDIT: Ok thx for clarifying. How about this?
bool sortPredicate(pair<SomeClass*, SomeClass*>& lhs, pair<SomeClass*, SomeClass*>& rhs)
{
if(lhs.first == rhs.first)
{
return rhs.second < lhs.second;
}
else
{
return rhs.first < lhs.first;
}
}
You should define an operator<on your pair class. I assume that your pair holds item1 and item2. So:
template <class T>
class pair{
private:
T item1;
T item2
public:
// [...] other stuff goes here
// here the comparing
bool operator<(pair p){
return (item1 < p.item1 || (item1 == p.item1 && item2 < p.item2));
}
};
This solution assumes that the items have defined the < and the == operators.
I suppose I didn't meet what you were exactly looking for, but I recommend to overload the <, >, and == operators in your pair class.