std::find how does it work? operator== [duplicate] - c++

This question already has answers here:
How to use std::find() with vector of custom class?
(2 answers)
Closed last year.
If I have a class
class Point
{
public:
Point() {}
Point(int _col, int _row) : row(_row), col(_col) {}
int row, col;
};
how can I use std::find() to check whether the point is already in vector? DO I have to overload operator== ?
I am trying to do this
#include <algorithm>
if(std::find(v.begin(), v.end(), x) != v.end()) {
/* v contains x */
} else {
/* v does not contain x */
}
Almost every answer I find on Stack Overflow suggest using find to check whether the object is in std::vector but none of them explains whether it compares the pointer of objects or the actual values of the object.

The C++ standard (draft N3242) says (in section 25.2.5 [alg.find]) that std::find:
Returns: The first iterator i in the range [first,last) for which the following corresponding conditions hold: *i == value[...]. Returns last if no such iterator is found.
Your question of whether it will search based on the value or the address of the object depends on how operator== is implemented. The simple answer is: std::find will return an iterator to the object for which operator== returned true.
Usually, this will just be a value-based comparison (because operator== is usually implemented to compare the values of two objects), and so you should generally expect std::find to search the range for the value you've provided (not the address of the object you provided).
It's possible for operator== to be implemented such that it compares based on address, like so:
bool operator==(const Point& left, const Point& right) {
return &left == &right;
}
Using this operator== will compare addresses, and so std::find will search for an object that has the same address as the one you've provided. It's generally a bad idea to implement operator== like this, though. Most people would implement operator== like so:
bool operator==(const Point& left, const Point& right) {
return left.row == right.row && left.col == right.col;
}
which, when used with std::find, will compare Points based on their values.

Unless your types are PODs fundamental types, you will need to provide an equality function, member or not.
There are two fundamental versions of std::find, one that assumes an equality operator and the other uses an equality function you supply.
I recommend that you add operator== and operator< to any class that will be compared for equality or ordered.
Here's an updated version of your class:
class Point
{
int x; // These are private by default.
int y;
public:
Point(int new_x, int new_y) : x(new_x), y(new_y)
{ ; }
bool operator==(const Point& p) const
{
return (x == p.x) && (y == p.y);
}
};
The member method operator== allows comparison without exposing the values to friends or the public.
If you want to use a free standing comparison function, you will need to either make the values public or make the function a friend:
class Point
{
int x; // These are private by default.
int y;
public:
Point(int new_x, int new_y) : x(new_x), y(new_y)
{ ; }
friend bool operator==(const Point& a, const Point& b);
};
bool operator==(const Point& a, const Point& b)
{
return (a.x == b.x) && (a.y == b.y);
}
If you want to use the free standing function with std::find, the example would be:
std::vector<Point> point_container;
//...
Point p;
std::vector<Point>::const_iterator iter;
iter = std::find(point_container.begin(), point_container.end(),
p,
Equal_Points);
Where Equal_Points is a free standing function that can compare the members of two Points.

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.

C2678 when compiling. Visual Studio refers to xutility [duplicate]

This question already has answers here:
How to use std::find() with vector of custom class?
(2 answers)
Closed last year.
If I have a class
class Point
{
public:
Point() {}
Point(int _col, int _row) : row(_row), col(_col) {}
int row, col;
};
how can I use std::find() to check whether the point is already in vector? DO I have to overload operator== ?
I am trying to do this
#include <algorithm>
if(std::find(v.begin(), v.end(), x) != v.end()) {
/* v contains x */
} else {
/* v does not contain x */
}
Almost every answer I find on Stack Overflow suggest using find to check whether the object is in std::vector but none of them explains whether it compares the pointer of objects or the actual values of the object.
The C++ standard (draft N3242) says (in section 25.2.5 [alg.find]) that std::find:
Returns: The first iterator i in the range [first,last) for which the following corresponding conditions hold: *i == value[...]. Returns last if no such iterator is found.
Your question of whether it will search based on the value or the address of the object depends on how operator== is implemented. The simple answer is: std::find will return an iterator to the object for which operator== returned true.
Usually, this will just be a value-based comparison (because operator== is usually implemented to compare the values of two objects), and so you should generally expect std::find to search the range for the value you've provided (not the address of the object you provided).
It's possible for operator== to be implemented such that it compares based on address, like so:
bool operator==(const Point& left, const Point& right) {
return &left == &right;
}
Using this operator== will compare addresses, and so std::find will search for an object that has the same address as the one you've provided. It's generally a bad idea to implement operator== like this, though. Most people would implement operator== like so:
bool operator==(const Point& left, const Point& right) {
return left.row == right.row && left.col == right.col;
}
which, when used with std::find, will compare Points based on their values.
Unless your types are PODs fundamental types, you will need to provide an equality function, member or not.
There are two fundamental versions of std::find, one that assumes an equality operator and the other uses an equality function you supply.
I recommend that you add operator== and operator< to any class that will be compared for equality or ordered.
Here's an updated version of your class:
class Point
{
int x; // These are private by default.
int y;
public:
Point(int new_x, int new_y) : x(new_x), y(new_y)
{ ; }
bool operator==(const Point& p) const
{
return (x == p.x) && (y == p.y);
}
};
The member method operator== allows comparison without exposing the values to friends or the public.
If you want to use a free standing comparison function, you will need to either make the values public or make the function a friend:
class Point
{
int x; // These are private by default.
int y;
public:
Point(int new_x, int new_y) : x(new_x), y(new_y)
{ ; }
friend bool operator==(const Point& a, const Point& b);
};
bool operator==(const Point& a, const Point& b)
{
return (a.x == b.x) && (a.y == b.y);
}
If you want to use the free standing function with std::find, the example would be:
std::vector<Point> point_container;
//...
Point p;
std::vector<Point>::const_iterator iter;
iter = std::find(point_container.begin(), point_container.end(),
p,
Equal_Points);
Where Equal_Points is a free standing function that can compare the members of two Points.

c++ Sort Vector based on distance to external Point

I was wondering if there is a nice way to sort a vector based on some external value. For instance, I have a vector of k nearest neighbors to a point. I want to sort this vector based on their distance to the query point. The query point is not included in the results, and std::sort allows you to define a comparison function for two elements in the vector (rather than each element vs a fixed point). Is there any pre-built method to do this sort of sort? Or would I need to build my own custom sorting algorithm? Thanks
If you have some class Point and the point you want to sort against is
Point p
Also assume that points is defined as
std::vector<Point> points;
and you had some function dist that took 2 Point and returned the distance (e.g. Euclidean distance) like
double dist(const Point& lhs, const Point& rhs)
{
// compute Euclidean distance or whatever
}
Then you can use std::sort with a lambda function
std::sort(begin(points),
end(points),
[p](const Point& lhs, const Point& rhs){ return dist(p, lhs) < dist(p, rhs); });
Edit
If you do not have C++11 access, you need to define a functor
struct DistanceFunc
{
DistanceFunc(const Point& _p) : p(_p) {}
bool operator()(const Point& lhs, const Point& rhs) const
{
return dist(p, lhs) < dist(p, rhs);
}
private:
Point p;
};
Then you can sort in a similar way
std::sort(points.begin(), points.end(), DistanceFunc(p));
You don't need a different sorting algorithm. std::sort works perfectly fine with user-provided orders.
The only problem here seems to be your definition. It still is easy, use a lambda : [X] (Point A, Point B) { return distance(A,X) < distance(B,X); }
you could just use the method you're talking about with two points from the vector (including your external point inside the vector) and then make sure to pop_front/erase when you're done.
Using a function object:
struct closer : public binary_function<point, point, bool>
{
closer(point np):p(np)
{}
point p;
bool operator()(point x, point y) { return dist(x,p) < dist(y,p); }
};
sort(V.begin(), V.end(), closer(P));

Check if structure is not in vector

I have a vector of structs. I need to check if the struct is or is not in the vector. The entire struct, not any specific member. It throws me this error upon compile time:
binary '==' : no operator found which takes a left-hand operand of type 'NavigationNode'
(or there is no acceptable conversion)
My struct:
struct NavigationNode{
int x, y; //their x and y position on the grid
float f, g, h;
int parentGCost;
int value;
};
NavigationNode currentNode;
The vector
vector<NavigationNode> openList;
My find:
if (find(closedList.begin(), closedList.end(), currentNode) == closedList.end() )
{
}
You need to overload operator==.
As global function:
bool operator==( const NavigationNode& lhs, const NavigationNode& rhs )
{
// compare lhs and rhs
}
Or as member function:
bool operator==( const NavigationNode& other ) const
{
// compare this & other
}
You will have to write an equality operator for your custom type. Assuming all variables have to be the same for two NavigationNode objects to be the same, it should look something like this:
bool floatEqual(float a, float b)
{
// adapt this comparison to fit your use-case - see the notes below
static const int EPSILON = 0.00001; // arbitrarily chosen - needs to be adapted to the occuring values!
return std::abs(a – b) <= EPSILON;
}
bool operator==(NavigationNode const & a, NavigationNode const & b)
{
return a.x == b.x &&
a.y == b.y &&
floatEqual(a.f, b.f) &&
floatEqual(a.g, b.g) &&
floatEqual(a.h, b.h) &&
a.parentGCost == b.parentGCost &&
a.value == b.value;
}
Even if you could also do it as a member function of NavigationNode, the recommended way is to implement the operator== as a free function (that way, both parameters can take advantage of any possible implicit conversions).
Note on float comparison: Due to how floating point numbers are represented, it is not a trivial task to compare them. Just checking for equality might not give the desired results. See e.g. this question for details:
What is the most effective way for float and double comparison?
You need to overload the comparison operator.
If your intention of "==" is "are each of the values contained in my struct equal to the corresponding members in this other struct" then you can write that.
bool operator==(const NavigationNode& lhs, const NavigationNode& rhs)
{
return /* compare each member in here */
}

sorting a vector of structs [duplicate]

This question already has answers here:
Sorting a vector of custom objects
(14 answers)
Closed 9 years ago.
I have a vector<data> info where data is defined as:
struct data{
string word;
int number;
};
I need to sort info by the length of the word strings. Is there a quick and simple way to do it?
Use a comparison function:
bool compareByLength(const data &a, const data &b)
{
return a.word.size() < b.word.size();
}
and then use std::sort in the header #include <algorithm>:
std::sort(info.begin(), info.end(), compareByLength);
Just make a comparison function/functor:
bool my_cmp(const data& a, const data& b)
{
// smallest comes first
return a.word.size() < b.word.size();
}
std::sort(info.begin(), info.end(), my_cmp);
Or provide an bool operator<(const data& a) const in your data class:
struct data {
string word;
int number;
bool operator<(const data& a) const
{
return word.size() < a.word.size();
}
};
or non-member as Fred said:
struct data {
string word;
int number;
};
bool operator<(const data& a, const data& b)
{
return a.word.size() < b.word.size();
}
and just call std::sort():
std::sort(info.begin(), info.end());
Yes: you can sort using a custom comparison function:
std::sort(info.begin(), info.end(), my_custom_comparison);
my_custom_comparison needs to be a function or a class with an operator() overload (a functor) that takes two data objects and returns a bool indicating whether the first is ordered prior to the second (i.e., first < second). Alternatively, you can overload operator< for your class type data; operator< is the default ordering used by std::sort.
Either way, the comparison function must yield a strict weak ordering of the elements.
As others have mentioned, you could use a comparison function, but you can also overload the < operator and the default less<T> functor will work as well:
struct data {
string word;
int number;
bool operator < (const data& rhs) const {
return word.size() < rhs.word.size();
}
};
Then it's just:
std::sort(info.begin(), info.end());
Edit
As James McNellis pointed out, sort does not actually use the less<T> functor by default. However, the rest of the statement that the less<T> functor will work as well is still correct, which means that if you wanted to put struct datas into a std::map or std::set this would still work, but the other answers which provide a comparison function would need additional code to work with either.