c++ working with maps of structs [duplicate] - c++

This question already has answers here:
Operator< and strict weak ordering
(7 answers)
Closed 6 years ago.
Point is a struct of the form:
typedef struct Point {
int x;
int y;
bool operator<(const Point &other) const
{
return ((this->x < other.x) && (this->y < other.y));
};
bool operator!=(const Point &other) const
{
return ((this->x != other.x) || (this->y != other.y));
}
bool operator==(const Point &other) const
{
return ((this->x == other.x) && (this->y == other.y));
}
} Point;
and I'm using:
map<Point,int> points;
the map is initialized with {{0,0},1}. and the program used points.count(p) to check whether the point p is a key in the points map.
There's a problem and the program always returns yes! even for points not in the map. I mean if p is not a key in points, I'm getting points.count(p)==1 (and not 0).
Also, when using points.find(p) to get the iterator to check whether the received point is really ==0 (it is not), I'm getting a reference to a totally different point..
Any idea how to fix the problem?

Your operator<() is badly defined. Suppose I have a=Point{0,1} and b=Point{1,1}. Then neither a<b nor b<a nor a==b is true, which makes the operator not an ordering on the set of possible points. In order to correct this you need to make one of the dimensions (say x) the 'major' dimension in your comparison:
bool operator<(const Point &other) const
{
return ((this->x < other.x) ||
((this->x == other.x) && (this->y < other.y));
};

Related

Invalid Operands to binary expression(const point and const point

map<pair<int,int>,int>pairOfNumbers;
pairOfNumbers.insert(pair<pair<int,int>,int>({1,2},2));
this is working, but
map<pair<point,point>,int>PointsOnLine;
PointsOnLine.insert(pair<pair<point,point>,int>(make_pair(points[i],points[j]),count));
this doesn't.
point is just a structure of two ints x and y;
I keep getting the error 'Invalid Operands to binary expression(const point and const point' this is the structure of point.
struct point
{
int x;
int y;
public:
bool operator==(const point& p)
{
if(x==p.x && y==p.y)
return true;
else
return false;
}
bool operator!=(const point& p)
{
if(x==p.x &&y==p.y)
return false;
else
return true;
}
};
how do I insert two points and distance between them in the map?
in Xcode I get this error
Your point type does not support weak-ordering. It has no method of determining is-less-than. You may think you don't need that because your point is actually tucked into a std::pair<point,point> but you do.
std::pair<T1,T2> supports weak ordering only if T1 and T2 do. In your case, they're the same type, so for a std::pair<point,point> to be used as key in a std::map<std::pair<point,pint>,T>, point must support weak ordering.
To support weak ordering, you must either provide an operator< that compares two of your objects in question, or a comparator functor type that does the same thing. The easiest way for you to do this would be:
#include <tuple>
struct point
{
int x;
int y;
bool operator <(const point& p) const
{
return std::tie(x, y) < std::tie(p.x, p.y);
}
bool operator ==(const point& p) const
{
return !(*this < p || p < *this);
}
bool operator !=(const point& p) const
{
return *this < p || p < *this;
}
};
I took liberty to redefine operator == and operator != to utilize the weak order properties of the proper operator <. It wasn't necessary, but ultimately it's just easier if operators root to as basic code as possible. With the above change you should be able to use both point and std::pair<point,point> as key types in std::map and std::set. In truth, a strict weak ordering can define all of the basic comparators (<=, >, >=, !=, ==) as derivations from operator < in one form or another (or relative to something that does). I challenge you to consider them, try implementing them, and above all, writing some test harnesses that verify your implementation.

Should I have to implement all 4 operator overload in order to deal with all const and non-const combinations?

General context
I have a self-made struct and I want to compare two instance of it. In order to do that I obviously overload the operator== so I will be able to do so. Now, this operator may be called with 0 to 2 const instances and 0 to 2 non-const instances.
As I want my operator == to compare 2 const as it compare any possible combination of const and non-const, the best for me should be to write only one overload which can deal with all possible combination. But as far as I know, I didn't find any way to do so.
Question
Does that mean that if I need to consider all possible combination, I have to write all 4 possible overloads ? Is there anyway I can avoid to write 4 times the same function only with const keywords changing ?
Specific example
So here is the struct. It represents an object on a plan, and consists of its position and a value associated to it:
struct Foo
{
int x;
int y;
double value;
};
Now let's say 2 Foo are equal if they have the same value and the same position. I have the following operator:
inline bool operator==(Foo object) // Compare two non-const Foo
{
return (x == object.x) && (y == object.y) && (value == object.value);
}
But, eww, unlucky some Foo can be constants, meaning that my objects can't move on the plan and can't change their value. And now I need to check if two const Foo can be equals and if a non-const Foo can be equal to a const Foo.
Is there anyway I can do that but still avoid to write those following functions which are almost the same as the first one ?
inline bool operator==(const Foo &object) // Compare a non-const Foo with a const Foo
{
return (x == object.x) && (y == object.y) && (value == object.value);
}
inline bool operator==(const Foo &object) const // Compare two const Foo
{
return (x == object.x) && (y == object.y) && (value == object.value);
}
inline bool operator==(Foo object) const // Compare a const Foo with a non-const Foo
{
return (x == object.x) && (y == object.y) && (value == object.value);
}
I don't have any requirements on c++ version. It can ever be c++17 or c++20.
Operator==, like most binary operators, should normally be implemented as a single, non-member, free function:
inline bool operator==(const Foo & a, const Foo & b ) {
return a.x == b.x && a.y == b.y && a.value == b.value;
}
If you have a non-const Foo object, you can use it where const Foo& object are expected, and you can call const-method on it, so you should only have one overload:
bool operator==(Foo const& object) const {
return (x == object.x) && (y == object.y) && (value == object.value);
}
You only needs to differentiate const and non-const overloads for specific cases where the behavior is different depending if the object is const or non-const, e.g., for operator[]:
// You want to return a reference on non-const object and a const-reference
// on const object, so you need both overloads.
X& operator[](std::size_t);
const X& operator[](std::size) const;
You usually want to have non-member functions for binary operators, with friend if necessary. In your case, since all members are public, you can simply create a free function (outside the struct):
bool operator==(Foo const& lhs, Foo const& rhs) const {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.value == rhs.vallue;
}
You can also drop that inline modifier which is kind of irrelevant nowadays, see, e.g., When should I write the keyword 'inline' for a function/method?.
You can also check What are the basic rules and idioms for operator overloading? for some idioms regarding operator overloading.
There is no reason to do so!
As long as it is just comparison then it is preferred always to use const references:
inline bool operator==(const Foo &object)const{
return (x == object.x) && (y == object.y) && (value == object.value);
}
The reason is you can pass the address or reference of const and non-const to a const member function but not the contrary.
There are some cases when overloading depending on constness matters.

C++: “invalid comparator” assert [duplicate]

This question already has answers here:
Operator< and strict weak ordering
(7 answers)
Closed 5 years ago.
Here is the code:
struct Payment
{
Payment(time_t time, float money) : mTime(time), mMoney(money) {}
bool operator==(const Payment& p) const // exact comparison
{
return mTime == p.mTime && mMoney == p.mMoney;
}
time_t mTime;
float mMoney;
};
std::vector<Payment> payments;
auto sortP = [](const Payment& p1, const Payment& p2) { return p1.mTime < p2.mTime || p1.mMoney <= p2.mMoney; };
std::sort(payments.begin(), payments.end(), sortP);
std::sort (not always, but sometimes, when mTime of two elements close to each other) raises invalid comparator assert in Visual Studio 2015. What's wrong with the code?
The problem is with the implementation of sortP. It does not satisfy the strictly weak ordering criteria. Read the details at https://www.boost.org/sgi/stl/StrictWeakOrdering.html.
I suggest the following change:
auto sortP = [](const Payment& p1, const Payment& p2)
{
// Order by mTime if they are not equal.
if ( p1.mTime != p2.mTime)
{
return p1.mTime < p2.mTime;
}
// Otherwise, order by pMoney
return ( p1.mMoney < p2.mMoney); // Use < not <=
};
You can use std::tie to make the implementation simpler.
auto sortP = [](const Payment& p1, const Payment& p2)
{
return std::tie(p1.mTime, p1.mMoney) < std::tie(p2.mTime, p2.mMoney);
};
|| p1.mMoney <= p2.mMoney should be || ((p1.mTime == p2.mTime) && (p1.mMoney < p2.mMoney)) Otherwise comparison will be wrong for case when p1.mTime is greater than p2.mTime while p1.mMoney is less than p2.Money. A good practice to ensure that such multi-field comparator satisfy strict weak ordering requirement is to write tests for all possible lt/gt combinations of fields.
Using c++11 your comparator lambda should look like:
#include <tuple>
...
auto sortP = [](const Payment& p1, const Payment& p2)
{
return std::tie(p1.mTime, p1.mMoney) < std::tie(p2.mTime, p2.mMoney);
};

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).

c++ std::set equality

I try to use a std::set in order to have unique elements in my container.
Since I have 3D objects :
Class Object3D{
private:
float x;
float y;
float z;
}
Those objects are equal when (A.x==B.x && A.y==B.y && A.z==B.z).
In std::set implementation a element A==B if (!(A < B) && !(B>A)).
It's impossible for my comparison... I have tried to overload == operator.
I chose set container for comparing values when I call insert(a).
I was doing something like that with std::vector v and his iterator:
if(!(A).inVector()){
v.push_back(A);
}
With
bool inVector(){
for(itr = v.begin();itr != v.end();itr++){
if(this->x==(*itr)->x && this->y==(*itr)->y && this->z==(*itr)->z){
return true;
}
}
return false;
}
Checking it for each object (10000-100000) is expensive in complexity.
Can someone have an idea ?
You need to implement a strict weak ordering < for your class. The easiest way is to use the lexicographic ordering provided by tuple:
#include <tuple>
class Object3D
{
public:
bool operator<(Object3D const & rhs) const
{
return std::tie(x, y, z) < std::tie(rhs.x, rhs.y, rhs.z);
}
// ...
};
#OP: std::set is a unique, ordered container. It requires either an operator< or a comparator passed explicitly, which implements a strict weak ordering.
If you don't want to impose an ordering on your elements, don't use an ordered container. You can use std::unordered_set if you just want to detect uniqueness without imposing an ordering.
You need to provide a comparator. You don't want to implement operator<, and I agree with that decision. You shouldn't provide meaningless functions for your class just to satisfy the constraints of some container. Thankfully, you don't need operator<. But you do need a function that has behavior similar to operator<. It doesn't have to mean that one object is considered less than another. It just needs to provide a strict-weak ordering. You can give it any name you want. For example:
bool Compare_by_x_then_y_then_z(const Object3D& lhs, const Object3D& rhs)
{
if (lhs.getX() != rhs.getX()) return lhs.getX() < rhs.getX();
if (lhs.getY() != rhs.getY()) return lhs.getY() < rhs.getY();
return lhs.getZ() < rhs.getZ();
}
You then provide this function to the constructor of your set:
typedef bool(*compT)(const Object3D&, const Object3D&);
std::set<Object3D,compT> objects(Compare_by_x_then_y_then_z);
You must declare operator<. You can do it like this
bool operator<(const Object3D& a, const Object3D& b)
{
if (a.x < b.x) return true;
if (b.x < a.x) return false;
if (a.y < b.y) return true;
if (b.y < a.y) return false;
if (a.z < b.z) return true;
if (b.z < a.z) return false;
return false;
}
It is arbitrary, but it doesn't really matter. As long as operator< gives a consistent ordering you'll be OK.
You have to provide a comparison operator, because std::set needs it for its implementation.
A simple less-than operator would look like this:
bool Object3D::operator<(const Object3D& other) const {
if(x != other.x) return x < other.x;
if(y != other.y) return y < other.y;
return z < other.z;
}