Elegant way to do sequential comparison (C++) - c++

Suppose I have a class with several member variables:
class MyClass{
std::string a;
int b;
SomeOtherClass c;
// some stuff...
public:
// some other stuff...
};
I want to define relational operators (operator<, etc.) that first compare a, but if the a are equal, compare b, but if the b are equal, compare c. (We assume SomeOtherClass already has relational operators defined.) So I have something like
bool operator==(MyClass param){
return (a == param.a) && (b == param.b) && (c == param.c);
}
bool operator<(MyClass param){
if(a < param.a) return true;
if(a > param.a) return false;
if(b < param.b) return true;
if(b > param.b) return false;
if(c < param.c) return true;
return false;
}
and so on. Is there any more elegant way to do this? It seems quite cumbersome, especially if there are lots of member variables to be compared. (Boost is an option.)

Yes, there's two ways I've seen commonly:
bool operator<(MyClass param){
if(a != param.a) return a<param.a;
if(b != param.b) return b<param.b;
return c<param.c;
}
http://coliru.stacked-crooked.com/view?id=dd70799c005e6e99c70ebda552161292-c96156d6cc95286981b0e9deef2eefae
or
bool operator<(MyClass param){
return std::tie(a, b, c)<std::tie(param.a, param.b, param.c);
}
http://coliru.stacked-crooked.com/view?id=00278eaca0d73b099fcd8edf87b5057b-c96156d6cc95286981b0e9deef2eefae

Sure, you can use std::tie for this:
#include <tuple>
bool operator<(const MyClass& lhs, const MyClass& rhs)
{
return std::tie(lhs.a, lhs.b, lhs.c) < std::tie(rhs.a, rhs.b, rhs.c);
}

Of course, you can use std::tie :
#include <tuple>
bool operator<(MyClass param){
return std::tie( a, b, c ) < std::tie( param.a, param.b, param.c );
}
It will create a tuple and after that, you just use the operator<.
This operator will compare each elements of the tuple.

Related

How do I establish a comparator for a set c++

I have a set, and for this set, I need two different comparators. For example, for a set frontier I need to sort by cost, but I have another set board which needs to be sorted by coordinates. I know you can define a comparator for each set using the comparator as the second argument, but I have tried this and it gave me an error.
The code I tried to use:
struct tile {
int id;
int xCord;
int yCord;
int cost;
...
bool operator<(const tile& Rhs) const {
if (cost < Rhs.cost) {
return true;
}
else if (cost < Rhs.cost) {
return false;
}
else {
if (id < Rhs.id) {
return true;
}
else
return false;
}
}
...
};
The other struct that I'm using for the comparator (I know this is most likely incorrect, which is why I'm asking for help.):
struct costComp {
int id;
int xCord;
int yCord;
int cost;
costComp() {}
costComp(int a, int b, int c, int d = 0) :
id(a),
xCord(b),
yCord(c),
cost(d) {}
bool operator<( const tile& Rhs) const {
if (xCord < Rhs.xCord)
return true;
else if (xCord < Rhs.xCord)
return false;
else {
if (yCord < Rhs.yCord)
return true;
else if (yCord < Rhs.yCord)
return false;
else
return false;
}
}
};
Then, I define the set as:
set<tile,costComp> startBoard;
The error I got:
c2064: term does not evaluate to a function taking 2 arguments
Any help is greatly appreciated.
the Compare parameter in std::set is intended to be some callable type that can be invoked with (const tile&, const tile&). This means you can use a functor that overloads operator(), for example, like this:
struct Comp {
bool operator()(const tile& lhs, const tile& rhs) const {
if (lhs.id < rhs.id) return true;
if (lhs.id > rhs.id) return false;
if (lhs.xCord < rhs.xCord) return true;
if (lhs.xCord > rhs.xCord) return false;
if (lhs.yCord < rhs.yCord) return true;
if (lhs.yCord > rhs.yCord) return false;
return lhs.cost < rhs.cost;
}
// or maybe, if this logic already exists:
bool operator()(const tile& lhs, const tile& rhs) const {
return lhs < rhs; // invoke tile::operator<(const tile&)
}
};
...
std::set<tile, Comp> myset;
This way, the comparator struct doesn't need to keep track of the details of any one tile object, and the redundant members of costComp can be removed.
If you want the comparator to be configurable, you can add members to the Comp struct definition and initialize them in a constructor call when you instantiate the set:
struct Comp {
Comp(bool use_cost = false /* default behavior */) : m_use_cost(use_cost) {}
bool operator()(const tile& lhs, const tile& rhs) const {
if (m_use_cost){
return lhs.cost < rhs.cost;
} else {
...
}
}
private:
const bool m_use_cost;
};
...
// default comparison, won't use cost
std::set<tile, Comp> setA;
// specify custom behaviour
std::set<tile, Comp> setB {Comp{true /* right here */}};
Obviously, the configurability is not limited to one or more bools. It might make sense to have some enum with values like SortByCost, SortByXcoord. Alternatively, you could have a separate functor struct that does each, but this means that sets with different comparators will have different types and will not be inter-copyable or moveable.

<set> with custom struct contains duplicates

I've been learning c++. I am stuck with this problem.
I have set that contains a custom struct that contains two long int's a & b. I have a custom comparer struct that compares the numbers and returns true if either a or b is different.
typedef long int li;
struct number {
number(li a1,li b1): a(a1), b(b1) {}
li a, b;
};
struct compare {
bool operator() (const number &lhs, const number& rhs) const{
return lhs.a != rhs.a || lhs.b != rhs.b;
}
};
int main() {
set<number, compare> nums;
nums.insert(number(1, 2));
nums.insert(number(1, 1));
nums.insert(number(2, 1));
nums.insert(number(1, 2));
for (auto &i : nums) {
cout << i.a << " " << i.b << endl;
}
return 0;
}
The output here is
1 2
2 1
1 1
1 2
It has two entries of 1 2. Any clarification would be appreciated.
Your comparison function should return whether some element is smaller than another, not whether or not they are equal. (More formally, it must define a "Strict weak ordering" on the elements of your set.)
Use something like
struct compare {
bool operator() (const number &lhs, const number& rhs) const{
return std::tie(lhs.a, lhs.b) < std::tie(rhs.a, rhs.b);
}
};
If you don't care about ordering, you may want to define a suitable hash function for your type and use std::unordered_set.
To avoid future problems like this, make sure to read the docs. They clearly explain what your comparison function is supposed to do.
For reference: std::tie as used above constructs tuples of references to its arguments which can then be compared lexicographically with <. This is an easy, generic and fast way to build some ordering for collections of less-than-comparable stuff.
Your comparison function needs to meet strict/weak ordering requirements.
(I actually prefer the answer using std::tie, but this may be more illustrative for newcomers)
bool compare(const number& lhs, const number& rhs)
{
if(lhs.a < rhs.a)
return true;
else if(lhs.a > rhs.a)
return false;
else
return lhs.b < rhs.b;
}

C++: How to use set_intersection on two vectors containing user-defined structs?

I have two vectors full of structs that are very simple:
typedef struct{
//three vertex ids
uint a,b,c;
} Face;
I'm currently trying to run set_intersection like so:
set_intersection(listOfFaces1.begin(),listOfFaces1.end(),listOfFaces2.begin(),listOfFaces2.end(), back_inserter(facesToDelete));
I'm guessing I need to overwrite some comparator somehow? But I'm not sure how to go about defining equality between two Face objects...
Any help would be much appreciated.
First of all, when you are programming in C++, you can just use:
struct Face {
uint a,b,c;
};
Here's a simple strategy for implementing operator< that works for the algorithms and containers in the standard library.
struct Face {
uint a,b,c;
bool operator<(Face const& rhs) const
{
if ( a != rhs.a )
{
return ( a < rhs.a);
}
if ( b != rhs.b )
{
return ( b < rhs.b);
}
return ( c < rhs.c);
}
};
or, as suggested by #Praetorian,
struct Face {
uint a,b,c;
bool operator<(Face const& rhs) const
{
return std::tie(a, b, c) < std::tie(rhs.a, rhs.b, rhs.c);
}
};

Implementing operator< in C++

I have a class with a few numeric fields such as:
class Class1 {
int a;
int b;
int c;
public:
// constructor and so on...
bool operator<(const Class1& other) const;
};
I need to use objects of this class as a key in an std::map. I therefore implement operator<. What is the simplest implementation of operator< to use here?
EDIT:
The meaning of < can be assumed so as to guarantee uniqueness as long as any of the fields are unequal.
EDIT 2:
A simplistic implementation:
bool Class1::operator<(const Class1& other) const {
if(a < other.a) return true;
if(a > other.a) return false;
if(b < other.b) return true;
if(b > other.b) return false;
if(c < other.c) return true;
if(c > other.c) return false;
return false;
}
The whole reason behind this post is just that I found the above implementation too verbose. There ought to be something simpler.
I assume you want to implement lexicographical ordering.
Prior to C++11:
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
bool Class1::operator<(const Class1& other) const
{
return boost::tie(a, b, c) < boost::tie(other.a, other.b, other.c);
}
Since C++11:
#include <tuple>
bool Class1::operator<(const Class1& other) const
{
return std::tie(a, b, c) < std::tie(other.a, other.b, other.c);
}
I think there is a misunderstanding on what map requires.
map does not require your class to have operator< defined. It requires a suitable comparison predicate to be passed, which conveniently defaults to std::less<Key> which uses operator< on the Key.
You should not implement operator< to fit your key in the map. You should implement it only if you to define it for this class: ie if it's meaningful.
You could perfectly define a predicate:
struct Compare: std::binary_function<Key,Key,bool>
{
bool operator()(const Key& lhs, const Key& rhs) const { ... }
};
And then:
typedef std::map<Key,Value,Compare> my_map_t;
It depends on if the ordering is important to you in any way. If not, you could just do this:
bool operator<(const Class1& other) const
{
if(a == other.a)
{
if(b == other.b)
{
return c < other.c;
}
else
{
return b < other.b;
}
}
else
{
return a < other.a;
}
}
A version which avoids multiple indentation is
bool operator<(const Class1& other) const
{
if(a != other.a)
{
return a < other.a;
}
if(b != other.b)
{
return b < other.b;
}
return c < other.c;
}
The "Edit 2" version of the author has on average more comparisons than this solution. (worst case 6 to worst case 3)
You could do:
return memcmp (this, &other, sizeof *this) < 0;
but that has quite a lot of of caveats - no vtbl for example and plenty more I'm sure.

How do you structure your comparison functions?

I frequently encounter situations, especially with sorting in C++, where I am comparing a series of fields in order to compare a larger structure. A simplified example:
struct Car{
Manufacturer make;
ModelName model;
Year year;
};
bool carLessThanComparator( const Car & car1, const Car & car2 ){
if( car1.make < car2.make ){
return true;
}else if( car1.make == car2.make ){
if( car1.model < car2.model ){
return true;
}else if( car1.model == car2.model ){
if( car1.year < car2.year ){
return true;
}
}
}
return false;
}
My instinctive approach seems cumbersome, especially for more than 3 fields. How would you structure this series of comparisons in C++? Do other languages provide a more succinct or elegant syntax?
Well, if your function hits a return in the if clause, there's no need for an explicit else, since it would have already bailed out. That can save on the "indent valley":
bool carLessThanComparator( const Car & car1, const Car & car2 ) {
if( car1.make < car2.make )
return true;
if ( car1.make != car2.make )
return false;
if( car1.model < car2.model )
return true;
if( car1.model != car2.model )
return false;
if( car1.year < car2.year )
return true;
return false;
}
I like MarkusQ's LISPish short-circuiting approach as well.
If this happens a lot you could put a template like this into a common header:
template<typename T, typename A1, typename A2, typename A3>
bool
do_less_than(
const typename T& t1,
const typename T& t2,
const typename A1 typename T::* a1,
const typename A2 typename T::* a2,
const typename A3 typename T::* a3)
{
if ((t1.*a1) < (t2.*a1)) return true;
if ((t1.*a1) != (t2.*a1)) return false;
if ((t1.*a2) < (t2.*a2)) return true;
if ((t1.*a2) != (t2.*a2)) return false;
return (t1.*a3) < (t2.*a3);
}
Add other templates for different numbers of arguments as required. For each less than function, you can then do something like this:
bool carLessThanComparator(const Car& car1, const Car& car2)
{
return do_less_than(car1, car2, &Car::make, &Car::model, &Car::year);
}
Personally I'd suggest NOT using the != or == operators like we seem to recommend here - this requires the arguments/members to have both less then and equal operators just to do a less then check on a class containing them - using just the less then operator is enought and will save you redundancy and potential defects in the future.
I suggest you write:
bool operator<(const Car &car1, const Car &car2)
{
if(car1.make < car2.make)
return true;
if(car2.make < car1.make)
return false;
if(car1.model < car2.model)
return true;
if(car2.model < car1.model)
return false;
return car1.year < car2.year;
}
I know it's an old question, but for future visitors: the modern C++11 solution is to use std::tie
struct Car{
Manufacturer make;
ModelName model;
Year year;
};
bool operator<(Car const& lhs, Car const& rhs)
{
return std::tie(lhs.make, lhs.model, lhs.year) < std::tie(rhs.make, rhs.model, rhs.year);
}
std::tie converts the struct into a std::tuple so that the above comparison operator delegates to std::tuple::operator<. This in turn does a lexicographical compare with respect to the order in which the members are marshalled into std::tie.
The lexicographic comparison is short-circuited in the same way as in the other solutions to this question. But it is even succinct enough to define on the fly inside a C++ lambda expression. For classes with private data members, it's best defined inside the class as friend function.
bool carLessThanComparator( const Car & car1, const Car & car2 ){
return (
( car1.make < car2.make ) or (( car1.make == car2.make ) and
( car1.model < car2.model ) or (( car1.model == car2.model ) and
( car1.year < car2.year )
)));
-- MarkusQ
Personally, I'd override the ==, <, >, and any other operators needed. That would clean up the code, not in the comparison, but when you need to make the comparison.
For the actual comparison itself, I would write it similarly to what Crashworks said.
bool operator<(const Car &car1, const Car &car2) {
if(car1.make < car2.make)
return true;
if(car1.make != car2.make)
return false;
if(car1.model < car2.model)
return true;
if(car1.model != car2.model)
return false;
return car1.year < car2.year;
}
I was wondering the same thing as the OP and stumbled upon this question. After reading the answers I have been inspired by janm and RnR to write a lexicographicalMemberCompare template function that only uses only operator< on the compared members. It also uses boost::tuple so that you can specify as many members as you want. Here it is:
#include <iostream>
#include <string>
#include <boost/tuple/tuple.hpp>
template <class T, class Cons>
struct LessThan
{
static bool compare(const T& lhs, const T& rhs, const Cons& cons)
{
typedef LessThan<T, typename Cons::tail_type> NextLessThan;
typename Cons::head_type memberPtr = cons.get_head();
return lhs.*memberPtr < rhs.*memberPtr ?
true :
(rhs.*memberPtr < lhs.*memberPtr ?
false :
NextLessThan::compare(lhs, rhs, cons.get_tail()));
}
};
template <class T>
struct LessThan<T, class boost::tuples::null_type>
{
static bool compare(const T& lhs, const T& rhs,
const boost::tuples::null_type& cons)
{
return false;
}
};
template <class T, class Tuple>
bool lexicographicalMemberCompare(const T& lhs, const T& rhs,
const Tuple& tuple)
{
return LessThan<T, typename Tuple::inherited>::compare(lhs, rhs, tuple);
}
struct Car
{
std::string make;
std::string model;
int year;
};
bool carLessThanCompare(const Car& lhs, const Car& rhs)
{
return lexicographicalMemberCompare(lhs, rhs,
boost::tuples::make_tuple(&Car::make, &Car::model, &Car::year));
}
int main()
{
Car car1 = {"Ford", "F150", 2009};
Car car2 = {"Ford", "Escort", 2009};
std::cout << carLessThanCompare(car1, car2) << std::endl;
std::cout << carLessThanCompare(car2, car1) << std::endl;
return 0;
}
Hope this is useful to someone.