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.
Related
Currently, to compare 3 or more integers, We do it this way. (a < b) && (b < c). I know that, a < b < c translates to (a < b) < c and compares boolean with integer. Is there any way such that, I can overload some operators on a custom Class to achieve continuous comparison? How does languages like python does this?
Update: According to accepted answer, I managed to write a piece of code. Have a look.
#include <iostream>
template <typename T>
class Comparator {
bool result;
T last;
public:
Comparator(bool _result, T _last) : result(_result), last(_last) {}
operator bool() const {
return result;
}
Comparator operator<(const T &rhs) const {
return Comparator(result && (last < rhs), rhs);
}
Comparator operator>(const T &rhs) const {
return Comparator(result && (last > rhs), rhs);
}
};
class Int {
int val;
public:
Int(int _val) : val(_val) {}
operator int() const {
return val;
}
Comparator<Int> operator<(const Int &rhs) {
return Comparator<Int>(val < int(rhs), rhs);
}
Comparator<Int> operator>(const Int &rhs) {
return Comparator<Int>(val > int(rhs), rhs);
}
};
int main() {
Int a(2), b(3), c(1), d(4), e(6), f(5);
std::cout << (a < b > c < d < e) << '\n';
// 2 < 3 > 1 < 4 < 6 > 5
return 0;
}
a < b < c is grouped as (a < b) < c.
If a or b are a type that you define, you could overload < for that type to return a proxy object, for which an overloaded < is also defined. That proxy object would contain the value of b along with the result of a < b.
It's some hassle, and will not make your code readable either since all C++ programmers know what a < b < c should do.
Python has its own syntax and interpreter.
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.
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;
}
I hope the title describes my problem completely.
Running the code I get an error:
error C2678: binary '==':no operator found which takes a left-hand operand of tpye 'A' (or there is no acceptable conversion)"
Where is the mistake and how can I fix the problem???
class A
{
private: //Dummy Values
int x;
int y;
}
class B
{
private:
vector <A> dataHandler;
public:
bool isElement(A element);
//Should return true if element exists in dataHandler
}
bool B::isElement(A element)
{
int length = dataHandler.size();
for(int i = 0; i<length; i++)
{
if(dataHandler[i] == element) //Check if element is in dataHandler
return true;
}
return false;
}
Within isElement you have
if(dataHandler[i] == element)
This is attempting to compare two A instances using operator==, but your A class doesn't implement any such operator overload. You probably want to implement one similar to this
class A
{
private: //Dummy Values
int x;
int y;
public:
bool operator==(A const& other) const
{
return x == other.x && y == other.y;
}
};
Also, isElement can be rewritten using std::find instead of a for loop
bool B::isElement(A const& element) const
{
return std::find(dataHandler.begin(), dataHandler.end(), element) != dataHandler.end();
}
Compiler tells you everything. Define operator== for class A. Update class A to something like this:
class A
{
private: //Dummy Values
int x;
int y;
public:
bool operator==(A const& rhs) const
{
return x == rhs.x && y == rhs.y;
}
};
you have to write your own == operator for class A, something like
bool operator==(const A &rhs) const
{
return this->x == rhs.x && this->y == rhs.y;
}
otherwise there's no way to know how to compare A objects.
You will have to implement the operator==.
Example of operator== (inline non-member function):
inline bool operator== (const A& left, const A& right){
return left.getX() == right.getX() && left.getY() == right.getY();
}
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.