add a non-comparable object to a set - c++

i have a class named Point (in an external library, i can't modify the code) that is used to represent a point in a 3d space:
int x = 0; int y = 0; int z = 0;
Point my_point p(x,y,z);
It overloads the == and != operators but not < or > ones. I need to store them in an efficient way (no double element, no repetition). I thought my data structure is set, but if i try to use, i get this error:
error: no match for ‘operator<’ in ‘__x < __y’
some advice?

Write a comparison operator, and instantiate the set with that:
struct ComparePoints
{
bool operator()( Point const& lhs, Point const& rhs ) const
{
if ( lhs.x != rhs.x ) {
return lhs.x < rhs.x;
} else if ( lhs.y != rhs.y ) {
return lhs.y < rhs.y;
} else {
return lhs.z < rhs.z;
}
}
};
std::set <Point, ComparePoints> mySet;

You can define a comparison functor and pass it as second template argument to std::set. See here, look at Compare. You can also define bool operator<(const Point& lhs, const Point& rhs), but if you cannot touch the class, this requires that the comparison can be implemented via the public interface of Point.

You can define operator < yourself. It doesn't have to be inside the Point class if x, y and z are available from Point's public interface.
bool operator<(const Point& lhs, const Point& rhs)
{
if( lhs.x != rhs.x ) return lhs.x < rhs.x;
if( lhs.y != rhs.y ) return lhs.y < rhs.y;
return lhs.z < rhs.z;
}

Related

std::map with key as a struct with three int members [duplicate]

This question already has an answer here:
Efficient operator< with multiple members
(1 answer)
Closed 1 year ago.
I would like to use a struct with three integer members as a key. How can I overload the < operator. I understand that for two members it could be overloaded as:
bool operator < (const CacheKey& a, const CacheKey& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
The generic solution is:
if (a.x != b.x) return a.x < b.x;
if (a.y != b.y) return a.y < b.y;
// ...
return false;
Or:
return std::tie(a.x, a.y) < std::tie(b.x, b.y);
(In this case, you might want to create a member function that returns the tied members, to be able to do something like a.tie() < b.tie() for all needed operators.)
Or, in C++20, you would add following to your class to automatically get all comparison operators including <:
auto operator<=>(const CacheKey &) const = default;
The most direct approach would be:
class Foo {
friend bool operator<(const Foo&, const Foo&);
int a, b, c;
}
bool operator<(const Foo& lhs, const Foo& rhs) {
return (lhs.a < rhs.a) ||
(lhs.a == rhs.a && lhs.b < rhs.b) ||
(lhs.a == rhs.a && lhs.b == rhs.b && lhs.c < rhs.c);
}

Finding extreme points in contours with OpenCV C++

I have tried to implement this code, but I have a trouble when I want to determine the most extreme point of the contour follow this tutorial.
# determine the most extreme points along the contour
extLeft = tuple(c[c[:, :, 0].argmin()][0])
extRight = tuple(c[c[:, :, 0].argmax()][0])
extTop = tuple(c[c[:, :, 1].argmin()][0])
extBot = tuple(c[c[:, :, 1].argmax()][0])
Can anyone help me to solve this problem?
Starting from a std::vector<cv::Point>, you can use std::max_element and std::min_element with an appropriate comparator, that works on x coordinates to find left and right points, and works on y coordinates to find top and bottom points:
// Your points
vector<Point> pts;
...
Point extLeft = *min_element(pts.begin(), pts.end(),
[](const Point& lhs, const Point& rhs) {
return lhs.x < rhs.x;
});
Point extRight = *max_element(pts.begin(), pts.end(),
[](const Point& lhs, const Point& rhs) {
return lhs.x < rhs.x;
});
Point extTop = *min_element(pts.begin(), pts.end(),
[](const Point& lhs, const Point& rhs) {
return lhs.y < rhs.y;
});
Point extBot = *max_element(pts.begin(), pts.end(),
[](const Point& lhs, const Point& rhs) {
return lhs.y < rhs.y;
});

What is wrong with my Operator <?

I get an assertion failure about my Vec2's Operator <, I have no idea what is wrong though.
bool Vec2::operator<( const Vec2& v ) const
{
if(x < v.x)
return true;
else
return y < v.y;
}
Invalid Operator < for std set insert
template<class _Pr, class _Ty1, class _Ty2> inline
bool __CLRCALL_OR_CDECL _Debug_lt_pred(_Pr _Pred, const _Ty1& _Left, const _Ty2& _Right,
const wchar_t *_Where, unsigned int _Line)
{ // test if _Pred(_Left, _Right) and _Pred is strict weak ordering
if (!_Pred(_Left, _Right))
return (false);
else if (_Pred(_Right, _Left))
_DEBUG_ERROR2("invalid operator<", _Where, _Line);
return (true);
}
Thanks
The problem is that this operator does not satisfy the weak ordering. For example consider two points
( 2, 1 ) and ( 1, 2 )
( 2, 1 ) is less ( 1, 2 ) because the second value 1 is less than 2.
At the same time ( 1, 2 ) is also less than ( 2, 1 ) because the first value 1 is less than the first value 2.
Look how thsi operator is defined for standard class std::pair and use the same operator.
The corrected way to satisfy the ordering is :
bool Vec2::operator<( const Vec2& v ) const
{
if(x < v.x)
return true;
if(x > v.x)
return false;
else
return y < v.y;
}
Or (code golf mode) :
bool Vec2::operator<( const Vec2& v ) const
{
return (x != v.x)? (x < v.x)
: (y < v.y) ;
}
Generally for a comparison like this, you want to compare the first pair of items, then if and only if those are equal, compare the second pair (and so on).
if (x < v.x)
return true;
if (x > v.x)
return false;
return y < v.y;
operator < should satisfy the Strict weak ordering.
A short way to do the job:
bool Vec2::operator< (const Vec2& v) const
{
return std::tie(x, y) < std::tie(v.x, v.y);
}

Check if point is inside vector

I want to check if a given point with x and yvalue is inside a vector of points:
bool inside_vector(int x, int y, vector<Point2i>& points)
{
for(vector<Point2i>::const_iterator it = points.begin();
it != points.end();
++it)
{
if(it->x == y && it->y == y)
{
return true;
}
}
return false;
}
Are there other approaches without the for loop?
You can use std::find or std::find_if with suitable functor to avoid writing your own loop. But you don't gain in terms of complexity: it is still O(n). For example,
bool operator==(const Point2i& lhs, const Point2i& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y;
}
Point2Di somePoint = Point2Di(x,y); // point to find
auto it = std::find(points.begin(), points.end(), somePoint);
or, without the equality operator,
auto it = std::find_if(points.begin(),
points.end(), [&somePoint](const Point2Di& p)
{return somePoint.x == p.x && somePoint.y == p.y;});
Assumming you have an operator== defined for your Point2i structure, you can use std::find(), like this:
std::vector<Point2i>::const_iterator findIt = std::find(
points.begin(),
points.end(),
Point2i(x, y));
I also assume you have a Point2i constructor that takes the two coordinates.

How to find an element in a vector?

I have defined a structure Coord as
struct Coord {
int x;
int y;
int z;
};
Overloaded operator!= for Coord
bool Coord::operator !=(Coord& crd)const {
if(this->x != crd.x)
{
if(this->y != crd.y)
{
if(this->z != crd.z)
return true;
else
return false;
}
else
return false;
return true;
}
else
return false;
}
Then initialized a vector variable as
vector<Coord> vcoord;
Now I am using following code to get index of vector having a perticular Coord object
int Index::getVIndex(Coord crd) {
vector<Coord>::iterator it;
int indx;
it = vcoord.begin();
while(it != vcoord.end() && (*it) != crd)
++it;
indx = distance(vcoord.begin(),it);
cerr << (*it).x << " " << (*it).y << " " << indx << endl;
return indx;
}
But the value of indx is always 0. Kindly help to get correct result.
You need a not-equals operator for your Coord struct in order to be able to do this:
(*it) != crd
The logic of your not-equals operator is incorrect. The best and easiest option is to provide an equality comparison and use std::find:
struct Coord {
int x;
int y;
int z;
};
bool operator == (const Coord& lhs, const Coord& rhs)
{
return lhs.x==rhs.x && lhs.y==rhs.y && lhs.z==rhs.z;
}
You can then implement != in terms of ==, but you don't need it if you use std::find, which uses == by default:
vector<Coord>::iterator it = std::find(vcoord.begin(), vcoord.end(), crd);
Your != operator returns true only if all coordinates differ; it should return true if any differ. This means your function will return zero if any coordinate of the first element matches the function argument's.
Your version is a long-winded way of writing:
return x != crd.x && y != crd.y && z != crd.z;
when it should be:
return x != crd.x || y != crd.y || z != crd.z;
It may be easier to get the logic correct by implementing it in terms of ==:
bool operator==(Coord const & lhs, Coord const & rhs) {
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}
bool operator!=(Coord const & lhs, Coord const & rhs) {
return !(lhs == rhs);
}
Also, given a definition of ==, you can use std::find rather than rolling your own loop:
auto found == std::find(vcoord.begin(), vcoord.end(), crd);
if (found == vcoord.end()) {
// Decide what to do if not found.
// Returning zero is a bad idea, since there's no way distinguish that
// from a successful outcome.
} else {
return std::distance(vcoord.begin(), found);
}
You incorrectly implemented the logic in the inequality operator.
It should be
bool Coord::operator !=(const Coord& crd)const {
return x != crd.x || y != crd.y || z != crz.z;
}
Your implementation is logically equivalent to
return x != crd.x && y != crd.y && z != crz.z;