For example, take a std::set of std::pair<int,int>, if you insert 2 pairs, {1,1} and {1,2}, both of them will be inserted in the set, however when i implemented a pair class, this behaviour is not seen. Only one of them gets inserted in the set. Please explain why?
#include <bits/stdc++.h>
using namespace std;
class PAIR {
public:
int x, y;
bool operator<(const PAIR& p) const { return x < p.x; }
};
int main() {
set<PAIR> s;
s.insert({ 1,1 });
s.insert({ 1,2 });
cout << s.size();
}
The output comes out to be 1, unlike i expected
std::set uses operator< for ordering and equivalence (it assumes equivalence when a<b and a>b are both false).
So with an operator< like x < p.x you're saying that only x is participating in comparison and y doesn't matter.
To use both x and y, the comparison should look more like:
bool operator<(const PAIR& p) const {
return std::tie(x, y) < std::tie(p.x, p.y);
}
A set can only hold unique values. It uses operator< to determine uniqueness.
For two items a and b, if (!(a<b) && !(b<a)) then they are considered equivalent.
Your operator< only considers the x member of your pair, so the y member is completely ignored for purposes of your set.
Related
I need a structure in which I can insert elements, have no duplicate, use a custom comparer and have the smallest element first. I tried using std::priority_queue, but the problem is that I get a lot of duplicates and I run out of space. So I thought about using std::set : std::set< std::pair<Coordinates, int>, Compare> positions; where
Coordinates
{
public:
Coordinates(int x = 0, int y = 0, char tool = 'T') : x(x), y(y), tool(tool) {}
public:
int x, y;
char tool;
};
class Compare
{
public:
bool operator() (const std::pair<Coordinates, int>& c1, const std::pair<Coordinates, int>& c2) const
{
return c1.second < c2.second;
}
};
I want the elements to be sorted based on the second element of pair, which this implementation is doing, but the problem is that it is using the same comparer when inserting new pairs and I get duplicates. My question is: Is it possible to make the std::set to not allow duplicates also to order the elements based on the second element of pair?
Edit: Eliminated some code that was not necessary and changed in Compare > with <
Using your Comparer the set will contain only unique values of the int, since Coordinates isn't participating in the comparison at all.
std::set uses operator < for sorting as well as equality; equality is determined as !(a<b || b<a). Therefore operator < should take into account every attribute which makes the element unique.
You can specialize std::less for your type like this:
namespace std {
template<>
struct less<pair<Coordinates, int>> {
bool operator()(const pair<Coordinates, int>& a, const pair<Coordinates, int>& b) const {
return tie(a.second, a.first.x, a.first.y) < tie(b.second, b.first.x, b.first.y);
}
};
}
Then std::set< std::pair<Coordinates, int>> positions; should work.
The issue here is that since you only look at second in you comparator, you can only store pairs that have unique values for second. This is because the set only uses the comparator to compare the elements. It doesn't use your operator == to check for equality but instead does cmp(a, b) == cmp(b, a)1 to test if the values are equal.
If you wan to sort by second, but allow other points with the same second but different other values then you need to add those values into you comparator. The easiest way to do that is to use std::tie to build a couple of tuples and use the tuples operator < which "does the right thing". That would look like
class Compare
{
public:
bool operator() (const std::pair<Coordinates, int>& c1, const std::pair<Coordinates, int>& c2) const
{
return std::tie(c1.second, c1.first.x, c1.first.y) < std::tie(c2.second, c2.first.x, c2.first.y);
}
};
1: If a is not less then b, and b is not less than a then a and b must be equal
As stated, the issue was that you only looked at the second member of the pair, so the set didn't care if the Coordinates were different. You simply needed to include the Coordinates in your comparison.
Unlike the other answers, this one utilizes a lambda for the comparison. I prefer it over std::tie and mucking around with std overrides. It also saves you the trouble of writing up a functor yourself like you did with your Compare class.
#include <iostream>
#include <set>
class Coordinates {
public:
Coordinates(int x = 0, int y = 0, char tool = 'T') : x(x), y(y), tool(tool) {}
int x, y;
char tool;
};
int main() {
using CoordPair = std::pair<Coordinates, int>;
auto compare = [](const CoordPair& a, const CoordPair& b) {
if (a.second != b.second)
return a.second < b.second;
// Replace this with some method of comparing Coordinates
return a.first.x != b.first.x || a.first.y != b.first.y;
};
std::set<std::pair<Coordinates, int>, decltype(compare)> list(compare);
list.emplace(Coordinates(1, 1), 2);
list.emplace(Coordinates(2, 0), 2);
list.emplace(Coordinates(1, 1), 3);
list.emplace(Coordinates(1, 1), 2); // Shouldn't show up
for (auto i : list)
std::cout << '(' << i.first.x << ", " << i.first.y << ", " << i.first.tool
<< ')' << ", " << i.second << '\n';
}
Your C++ version wasn't specified, this needs at least C++11.
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.
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.
#include<bits/stdc++.h>
using namespace std;
struct segment{
int a;
int b;
int c;
bool const operator<(const segment &o) const {
return a < o.a;
}
};
int main()
{
map<segment,int> myMap;
map<segment,int>::iterator it;
struct segment x,y,z;
x.a=2;
x.b=4;
x.c=6;
y.a=2;
y.b=5;
y.c=8;
z.a=2;
z.b=4;
z.c=6;
myMap[y]++;
myMap[z]++;
myMap[x]++;
for( it =myMap.begin(); it != myMap.end(); it++)
cout<<(*it).first.a<<" "<<(*it).second<<endl;
return 0;
}
it gives result as
2 3
but i want it to print
2 1
2 2
In short I want to increment the value of the map if exactly the same struct instance is fed instead of making a new copy
IMO the best way to compare multiple members is using std::tie as it is much harder to mess up:
bool const operator<(const segment &o) const {
return std::tie(a, b, c) < std::tie(o.a, o.b, o.c);
}
Edit: Would just like to add this link to cppreference as the example there is almost exactly your question.
You can change your less operator to:
bool const operator<(const segment &o) const {
return a < o.a || (a == o.a && b < o.b) || (a==o.a && b==o.b && c < o.c) ;
}
This compares the values in the order of a, b, c.
But you can change it anyway you want to compare the structure.
As far as your map is concerned, there is only one unique object here. In terms of the comparison you specified, and the implied equivalence, x == y and y == z. Why? Neither of them is smaller than the other, so, according to STL logic by comparison, they must be equivalent.
Perhaps you're looking for a std::multimap.
Alternatively, if you want to define inequality (and hence implied equivalence) in terms of all the members, you could do something like this:
#include <tuple>
bool const operator<(const segment &o) const {
return std::make_tuple(a, b, c) < std::make_tuple(o.a, o.b, o.c);
}
P.S. You should avoid including stuff from bits, as you're including stuff from the implementation. Instead, try to use stuff like
// See? no bits.
#include <map>
I attempted to do something like this but it does not compile:
class point
{
public:
int x;
int y;
};
int main()
{
vector<point> vp1;
vector<point> vp2;
vector<point> vp3;
map < vector<point>, int > m;
m[vp1] = 1;
m[vp2] = 2;
m[vp3] = 3;
map < vector<point>, int >::iterator it;
for (it=m.begin(); it!=m.end(); it++)
{
cout<<m[it->first]<<endl;
}
return 0;
}
You can use anything as the index type into a std::map as long as it supports an operator< (which could could define as a free-standing function -- doesn't have to be a member function, as long as you can write a < b for a and b being instances of your type of interest) with the usual semantics (antireflexive, transitive, ...). Or, you can pass a binary function with the same semantics to use in lieu of <, if that suits you better.
You can, but the type used as a key in a map needs to be comparable, either using operator<, or using a comparison function/functor you supply as the third template parameter for the map type.
Yes, you can. Vectors, like all containers, are comparable. The resulting map will sort the vectors in lexicographic order.
The problem is that point is not comparable. You have to define a sort order for points, and then this will in turn define lexicographic order over vector<point>.
class point
{
public:
int x;
int y;
};
bool operator<( point const &l, point const &r ) {
return l.x < r.x? true
: r.x < l.x? false
: l.y < r.y;
}
A simpler solution is to use std::pair instead of defining your own point.
typedef pair< int, int > point; // point::first = x, point::second = y
// pair is already comparable; order defined as in previous example
typedef vector<point> pointvec; // OK
You haven't defined a function to compare a vector<point> Maps make requiremets of keys checking of equivalence and comparison.
You have to declare the operator<. It would look like this (please keep in mind, that the three vectors in your sample code actually look the same):
bool operator<(const vector<point>& left, const vector<point>& right)
{
return left.size() < right.size();
}