C++ std::set Find function overloading == operator - c++

I am using sets. I use a custom struct as the key. I am inserting a value and trying to find the inserted value. But it never seems to find the element.
I have overridden both the == operator and the < operator.
Here is the code of the structure:
struct distance_t
{
public:
int id;
double distance;
bool operator<(const distance_t& rhs) const
{
if(distance < rhs.distance)
return true;
else
return false;
}
bool operator==( const distance_t& rhs)
{
if(id == rhs.id)
return true;
else
return false;
}
};
And this is the code of main
int main()
{
set<distance_t> currentSet;
distance_t insertDistance;
insertDistance.id =1;
insertDistance.distance = 0.5;
currentSet.insert(insertDistance);
distance_t findDistance;
findDistance.id = 1;
assert(currentSet.find(findDistance) != currentSet.end());
}
It always fails in the assert statement. What am I doing wrong?
Edit -Ok now I understand that it does not use the == operator at all. Here is what I want. I need the data structure to be ordered by distance. But I should be able to remove it using the id. Is there any clean way or already existing datastructure to do this?

It fails because your less-than comparison uses distance_t::distance, which you are not setting in findDistance:
distance_t findDistance;
findDistance.id = 1;
std::set does not use operator== for anything. It only uses operator<. So you would have to change it's logic to use distance_t::id.
If you want to search by id without changing the set's ordering, you can use std::find:
set<distance_t>::iterator it = std::find(currentSet.begin(),
currentSet.end(),
findDistance);
This will use your operator==. Bear in mind that this has linear time complexity.

Because operator== is not invoked at all. Comparing elements is like:
!(a < b) && !(b < a)
In other words, it uses operator<.

As you haven't assigned a value to findDistance.distance the result of the less then comparison is undefined.
Note that your definitions of the equality and less then comparison operators is dangerous, because it is easy to define instances of distance_t where their result is inconsistent. One example is two instances with the same distance but different id's.

Related

Why is operator< overloading necessary for STL set containing custom objects, instead of operator==

I'm working with C++11 and I want to use set to store my custom objects because I need a container, which can filter the elements with the same value.
Here is the class of my custom object:
struct Ele
{
int a, b, c;
}
As my understanding, I need to overload the function operator== because set need to filter the elements with the same value.
However, after reading this link: How do i insert objects into STL set, it seems that overloading operator< is what I need, instead of operator==.
So I code like this:
struct Ele {
int a, b, c;
friend bool operator<(const Ele &e1, const Ele &e2);
};
bool operator<(const Ele &e1, const Ele &e2)
{
if (e1.a < e2.a) {
return true;
}
if (e1.a == e2.a) {
if (e1.b < e2.b) {
return true;
}
if (e1.b == e2.b) {
if (e1.c < e2.c) {
return true;
}
return false;
}
return false;
}
return false;
}
And do a test as below:
set<Ele> myset;
Ele e1;
e1.a = 1;
e1.b = 2;
e1.c = 3;
Ele e2;
e2.a = 1;
e2.b = 2;
e2.c = 3;
myset.insert(e1);
myset.insert(e2);
cout << myset.size() << endl;
Well, the output is 1, instead of 2, which means that the insertion for e2 failed as expected because the value of e2 is the same with the value of e1.
Now I'm confused.
As my understanding, operator< just told the compiler how to understand e1 < e2, how does compiler know how to understand e1 == e2? What if I want to set such a rule: e1 == e2 only if e1.a == e2.b && e1.b == e2.c && e1.c == e2.a?
== is not sufficient on its own to define an ordering, whereas < is. Furthermore all other relational operators can be cast in terms of operator<, so long as you are allowed to negate the result.
For example, a == b if !(a < b) and !(b < a)
< is a natural choice insofar that thinking of things being in ascending order is natural. > could have been picked, but it wouldn't have been so tractable.
operator< is required because std::map internally is implicitly required by the standard to be a tree-like data structure that requires some kind ordering between elements. Notice that one can generate all the comparing functions using just operator<. Take a look:
a == b <==> !(a < b) && !(b < a)
a > b <==> b < a
a >= b <==> !(a < b)
and so on
You could ensure uniqueness with == (and !), but it would be less efficient than using < (std::set) or a hash function (std::unordered_set).
Consider insert. With only == you would have to compare every element to verify that you don't have an equal element already. std::set's elements are kept in order, so insert does a binary search, looking at far fewer elements. std::unordered_set's elements are kept in buckets based on the hash value, so lookup only has to search the bucket, not the whole collection.
As noted in Bathsheba's answer, you can synthesize an equality function from <
The other answers already point out that std::set requires an ordering of the elements which requires operator< to be defined for the type. If you don't care about ordering, and only care about uniqueness, you can use std::unordered_set.
Also, here's a much cleaner way to implement the comparison:
bool operator<(const Ele &e1, const Ele &e2)
{
return std::tie(e1.a, e1.b, e1.c) < std::tie(e2.a, e2.b, e2.c);
}

Checking whether an element is in a C++ set is really slow

I'm implementing an algorithm that implies a lot of checking whether elements are in a set/list. I was using std::vector containers but time was increasing exponentially as the vector would grow.
I've decided I would try using std::set containers in order not to have to explore the entire container to know whether it contains a certain element.
I implemented the following function that checks whether an element is part of a given set:
bool in_set(set<Node> node_set){
return node_set.find(*this) != node_set.end();
}
However, that function is taking around 2s for very small sets (1-3 elements) which makes my entire algorithm unusable.
The custom class I'm using look like this:
class Node{
public:
int d;
int h_score;
int coordinates [3];
Node* parent_address;
};
The comparison operator that I implemented look like this:
bool operator<(Node other) const{
return concatenate(concatenate(this->coordinates[0], this->coordinates[1]), this->coordinates[2]) <
concatenate(concatenate(other.coordinates[0], other.coordinates[1]), other.coordinates[2]);
}
Edit: The concatenate function does not seem to take a lot of time while executing, it looks like this:
int concatenate(int i, int j) {
int result = 0;
for (int x = i; x <= j; x++) {
result = result * 10 + x;
}
return result;
}
Do you know why it is taking so much time, and more importantly, how to make it faster?
First of all, you can try to pass Set as const & and not in operator< also as const &.
bool in_set(const set<Node>& node_set){
return node_set.find(*this) != node_set.end();
}
And
bool operator<(const Node& other) const
It will use ref instead of a copy of your set and Node objects.
Do you know why it is taking so much time
concatenate(1, 100000000) takes 1.3 second on my raspberry pi, that way to do is too slow, and in fact useless
Note also that because of the possible overflows concatenate can give the same result for different nodes, this is non compatible for an operator<
how to make it faster?
you have to find something else than these calls of concatenate to implement your operator<
What is your need ? is the order in the set is important or it can be replaced by any one else ?
It is not mandatory to create a unique identifier to compare two nodes, compare them directly, for instance :
bool operator<(const Node & other) const{
if (coordinates[0] < other.coordinates[0])
return true;
if (coordinates[0] >= other.coordinates[0])
return false;
if (coordinates[1] < other.coordinates[1])
return true;
if (coordinates[1] >= other.coordinates[1])
return false;
return (coordinates[2] < other.coordinates[2]);
}
To understand that operator< works you can consider node.coordinates supports a big number having 3 times the size of an int, so I compare the higher bits, then if equals the medium bits, then if equals the lower bitsused for a set
Your operator< takes a copy of the Node. There's also no need to create strings to compare, the built-in tuple class can do that:
How about:
bool operator<(const Node& other) const {
return std::make_tuple(coordinates[0], coordinates[1], coordinates[2]) <
std::make_tuple(other.coordinates[0], other.coordinates[1], other.coordinates[2]);
}

C++ STL Binary Search (lower_bound, upper_bound)

I have implemented a binary search like this:
typedef std::vector<Cell>::iterator CellVectorIterator;
typedef struct _Point {
char x,y;
} CoordinatePoint;
typedef struct _Cell {
...
CoordinatePoint coordinates;
} Cell;
struct CellEqualityByCoordinates
{
bool
operator()(const Cell& cell1, const Cell& cell2) const
{ return cell1.coordinates.x == cell2.coordinates.x && cell1.coordinates.y == cell2.coordinates.y; }
};
CellVectorIterator FindCellByCoordinates (CellVectorIterator first, CellVectorIterator last, const Cell &val)
{
return std::upper_bound(first, last, val, CellEqualityByCoordinates());
}
But it doesn't always find a value.
What's wrong with that?
Your comparison function will not work for a binary search. It is not supposed to determine equality, it is supposed to determine an order relation. Specifically, it should return true if the first argument would definitively come before the second in a sorted range. If the arguments should be considered equal, or the second would come before the first, it should return false. Your range also needs to be sorted by this same criteria in order for the binary search to work.
An example function that might work:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
if (cell1.coordinates.x < cell2.coordinates.x) return true;
if (cell2.coordinates.x < cell1.coordinates.x) return false;
return cell1.coordinates.y < cell2.coordinates.y;
}
A similar example that doubles as a lesson in short-circuit boolean evaluation would be something like:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
return (cell1.coordinates.x < cell2.coordinates.x) ||
(!(cell2.coordinates.x < cell1.coordinates.x) &&
cell1.coordinates.y < cell2.coordinates.y);
}
Both exhibit a property called strict weak ordering. It is frequently required for various sorting and/or searches in standard library collections and search algorithms.
Yet another example utilizes a std::pair, which already has a proper std::less overload available that does the above, and thus makes this considerably less complicated:
bool operator()(const Cell& cell1, const Cell& cell2) const
{
return std::make_pair(cell1.coordinates.x, cell1.coordinates.y) <
std::make_pair(cell2.coordinates.x, cell2.coordinates.y);
}
A similar algorithm is available for tuples via std::tie.
Of course, all of this assumes you have an actual ordered sequence in the first place, ordered by the same comparison logic. (which we can only assume is true, as no evidence of such was posted).

Using a (mathematical) vector in a std::map

Related: what can I use as std::map keys?
I needed to create a mapping where specific key locations in space map to lists of objects. std::map seemed the way to do it.
So I'm keying a std::map on an xyz Vector
class Vector
{
float x,y,z
} ;
, and I'm making a std::map<Vector, std::vector<Object*> >. So note the key here is not a std::vector, its an object of class Vector which is just a math xyz vector of my own making.
To produce a "strictly weak ordering" I've written the following overload for operator<:
bool Vector::operator<( const Vector & b ) const {
// z trumps, then y, then x
if( z < b.z )
{
return true ;
}
else if( z == b.z )
{
if( y < b.y )
{
// z == b.z and y < b.y
return true ;
}
else if( y == b.y )
{
if( x < b.x )
{
return true ;
}
else if( x == b.x )
{
// completely equal
return false ;
}
else
{
return false ;
}
}
else
{
// z==b.z and y >= b.y
return false ;
}
}
else
{
// z >= b.z
return false ;
}
}
Its a bit long but basically makes it so any vector can consistently be said to be less than any other vector ((-1, -1, -1) < (-1,-1,1), and (-1, -1, 1) > (-1,-1,-1) for example).
My problem is this is really artificial and although I've coded it and it works, I am finding that it "pollutes" my Vector class (mathematically) with this really weird, artificial, non-math-based notion of "less than" for a vector.
But I need to create a mapping where specific key locations in space map to certain objects, and std::map seems the way to do it.
Suggestions? Out-of-box solutions welcome!!
Instead of defining operator< for your key class, you can give the map a custom comparator. This is a function object that takes two arguments and returns true if the first comes before the second. Something like this:
struct CompareVectors
{
bool operator()(const Vector& a, const Vector& b)
{
// insert comparison code from question
}
};
typedef std::map<Vector, Value, CompareVectors> VectorValueMap;
You can separate it from the class. Then specify it as the comparison operator for the std::map.
std::map<Vector,std::vector<Object*>,Compare> data;
Where Compare is a function (or functor) that can compare tow Vector objects.
I also think you can simplify your Compare operation.
bool Compare<( const Vector& lhs, const Vector& rhs)
{
// z trumps, then y, then x
if( lhs.z < rhs.z )
{ return true ;
}
else if (lhs.z > rhs.z)
{ return false;
}
// Otherwise z is equal
if( lhs.y < rhs.y )
{ return true ;
}
else if( lhs.y > rhs.y )
{ return false;
}
// Otherwise z and y are equal
if ( lhs.x < rhs.x )
{ return true;
}
/* Simple optimization Do not need this test
If this fails or succeeded the result is false.
else if( lhs.x > rhs.x )
{ return false;
}*/
// Otherwise z and y and x are all equal
return false;
}
Notice we test for less then greater and then fall through for equal. Personally I like the simplicity of this style. But I often see this being compressed like this:
bool Compare<( const Vector& lhs, const Vector& rhs)
{
// Note I use three separate if statements here for clarity.
// Combining them into a single statement is trivial/
//
if ((lhs.z < rhs.z) ) {return true;}
if ((lhs.z == rhs.z) && (lhs.y < rhs.y) ) {return true;}
if ((lhs.z == rhs.z) && (lhs.y == rhs.y) && (lhs.x < rhs.x)) {return true;}
return false;
}
I think std::tr1::unordered_map is just what you need. No strict weak ordering will be required. GCC has a something similar in tr1 namespace as well. Or go for Boost.Unordered.
The unordered counterparts of the more pedestrian map or set gives you two advantages:
You don't need to define a less-than operator where none makes sense
Hash tables may perform better than balanced binary trees, the latter being the preferred method of implementing the ordered map or set structures. But that depends on your data access pattern/requirements.
So, just go ahead and use:
typedef std::tr1::unordered_map<Vector, std::vector<Object *> > VectorMap;
This makes use of a default hash function that takes care of insertion/search for your map.
PS: the > > thingy will be fixed in the upcoming standard and hence future compiler versions.
It's normal that you find that your class is polluted by this. It's also polluted from a CS point of view.
The normal way of defining such an operator is through (potentially friend) free functions.
However the first question to ask yourself is: does it makes sense. The issue is that you have defined a method for your class that is only meaningful in a limited context but accessible everywhere. That's why the "pollution" feeling kicks in.
Now, if I were to need such mapping from a Vector to a collection of Objects, here are the questions I would ask myself:
Do I need the Vector to be ordered ? Yes: std::map, No: std::unordered_map or std::tr1::unodered_map or std::hash_map or boost::unordered_map.
Will this collection owns the Object ? Yes: boost::ptr_vector<Object> or std::vector< std::unique_ptr<Object> >, No: std::vector<Object*>
Now, in both cases (map and unordered_map), I will need something to transform my key. The collection provide a supplementary template argument which takes a Functor type.
Beware: as has been mentioned in another answer, floating point representation is awkward in a computer, therefore you will probably need to relax the meaning of equality and ignore the lower order digits (how many depends on your computations).
I think your approach is fine. If you're worried about polluting the Vector class, then I believe a stand-alone function will work just as well:
bool operator<( const Vector& lhs, const Vector& rhs )
{
...
}
But just a word of warning: what you're doing here is pretty risky. There are often errors in floating point calculations. Suppose you insert some point into your map. Then you calculate a point and check the map to see if it's there. Even if, from a strictly mathematical point of view, the second point is the same as the first, there's no guarantee that you'll find it in the map.

std::map::find()

I have a simple struct which i'm using as a key in a std::map
struct PpointKey{
unsigned int xp,yp; //pixel coordinates
unsigned int side;
PpointKey(unsigned xp,unsigned yp,unsigned side=5):xp(xp),yp(yp),side(side)
{}
bool operator==(const PpointKey& other) const{
const unsigned int x = other.xp;
const unsigned int y = other.yp;
return ((x>=xp && x<=xp+side) && (y>=yp && y<=yp+side));
}
bool operator<(const PpointKey& other) const{
const unsigned int x = other.xp;
const unsigned int y = other.yp;
const unsigned other_distance_2 = x*x + y*y;
const unsigned this_distance_2 = this->xp*this->xp + this->yp * this->yp;
return this_distance_2 < other_distance_2;
}
};
What I would like to achieve is to use the find() to access the map with a key that has its xp,yp attributes within a side distance. In other words, if I have an (x,y) tuple, I would like to find inside the map the first PpointKey that fulfils the condition inside the operator== function
return ((x>=xp && x<=xp+side) && (y>=yp && y<=yp+side));
Is this possible using find? I'm getting map.end(), so I would like to check wheter the find () function uses the operator==. Maybe the search algorithm would be better?
Thanks in advance.
The find function of map does not use the operator==.
However you can use std::find, passing in the begin() and end() iterator of map. It will simply iterate through the sequence one at a time and yield the first object that matches (complexity is linear).
The issue you encounter is due to the fact that you have abused operator overload. The problem here is that the common definition of operator== is:
T operator==(T lhs, T rhs)
{
return !(lhs < rhs) && !(rhs < lhs);
}
And this is not the case with your definition, thus you cannot substitute one for the other.
It would be best if you used traditional functions with expressive names rather than operator overloading, it would be less misleading. Note that map and std::find allow you to pass suitable predicate objects, you don't need to overload the operators to use them.