C++ Map<>.find() overloading for different class - c++

I try to use a map which is defined as :
map<Vertex,unsigned int> _addedVertices;
now when I use the find function to check if a vertex is already inside
I get an iterator to a wrong vertex with different information, so I have tried the following :
map<Vertex,unsigned int,cmpByVertexFields> _addedVertices;
which didn't help.
also I have the following overloaded functions inside the Vertex class.
bool operator<(const Vertex &otherV)const{
return(_x<otherV._x && _y<otherV._y && _z<otherV._z);
}
bool operator==(const Vertex &otherV)const{
return _x==otherV._x && _y==otherV._y && _z==otherV._z;
}
but nothing works.
Example:
I've inserted a vertex containing (0.2,0.1,0.4)
and next thing I use is the find function with (0.2,0.15,0.41)
the iterator I get is of the first vertex instead of map.end().
What did I forget to define?
Thanks
edit: cmpByVertexFields :
struct cmpByVertexFields {
bool operator()(const Vertex& a, const Vertex& b) const {
return a.getX()==b.getX() &&
a.getY()==b.getY() &&
a.getZ()==b.getZ();
}
};

This is your culprit
bool operator<(const Vertex &otherV)const{
return(_x<otherV._x && _y<otherV._y && _z<otherV._z);
}
This doesn't yield strict weak ordering.
You need something like this
bool operator<(const Vertex &otherV)const{
if(_x != otherV.x)
return _x < otherV.x;
if(_y != otherV.y)
return _y < otherV.y;
return _z < otherV.z;
}
Or, equivalently and more conveniently, compare them as tuples, using std::tie
bool operator<(const Vertex &otherV)const{
return std::tie(x_, y_, z_) < std::tie(OtherV.x_, OtherV.y_, OtherV.z_);
}

As Juan said in a comment, your operator < implementation is semantically incorrect. Since you’re talking about vertices, you actually need to implement a lexicographical comparison across _x, _y and _z.
The easiest way is to use the std::tuple built-in comparison:
bool operator<(const Vertex &otherV)const{
return std::tie(_x, _y, _z) < std::tie(otherV._x, otherV._y, otherV._z);
}
Using std::tie in this way is now the established way of implementing a lexicographical comparison across (member) variable (and you can actually use the same for the operator== implementation).

The comparison functor or less than operator< must implement a strict weak ordering. This is a requirement for an std::map. Yours does not. There is no obvious and natural way to order 3D vertices, but if you want to order lexicographically by x, y and then z coordinate, then the easiest way is to use std::tie (or boost::tie or std::tr1::tie if you do not have C++11 support):
bool operator<(const Vertex &otherV)const{
return std::tie(_x, _y, _z) < std::tie(otherV._x, otherV._y, otherV._z);
}
Note that this ordering is completely arbitrary: why would x take precedence over y? It is up to you to implement the ordering that suits the problem you are trying to solve. On the other hand, if you don't care about the actual ordering of the elements of the map, any strict weak ordering will do.

Related

Efficient way to remove duplicates

Following the answer in this thread "What's the most efficient way to erase duplicates and sort a vector?". I wrote the following code, but I got an error complaing no match for ‘operator<’ (operand types are ‘const connector’ and ‘const connector’) blahblah...
connector is a class I wrote myself, it basically is a line with two geometry points. uniqCntrs is a std::vector. It has 100% duplicates in it, which means each element has a duplicate, the size of uniqCntrs is quite big. What's wrong with my code, and how to deal with this situation?
std::set<connector> uniqCntrsSet;
for(unsigned int i = 0; i < uniqCntrs.size(); ++i )
{
uniqCntrsSet.insert(uniqCntrs[i]);
}
uniqCntrs.assign(uniqCntrsSet.begin(), uniqCntrsSet.end());
Edit:
I have no idea how to define < operator for my connector class. I mean it is physically meaningless to say one line is smaller than the other.
From cppreference:
std::set is an associative container that contains a sorted set of unique objects of type Key. Sorting is done using the key comparison function Compare.
The second template argument of std::set, Compare, is defaulted to std::less which by defaults compares the objects with operator<. To fix the issue you can simply define operator< for your Key type (connector that is).
Actually the operator< is just used to efficiently order the map which is used by std::set. It does not need to make any sense. The only requirement is that the operator satisfy the standard mathematical definition of a strict weak ordering.
Look at this point example:
class Point
{
public:
Point(int x, int y) : x(x), y(y) {
}
public:
bool operator==(const Point &other) const {
return x==other.x && y==other.y;
}
bool operator!=(const Point &other) const {
return !operator==(other);
}
bool operator<(const Point &other) const {
if (x==other.x) {
return y<other.y;
} else {
return x<other.x;
}
}
private:
int x;
int y;
};

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

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.

std::map unique std::less<> function for a 2D point as key

Well, after four hours of debugging, confused as I could be, I found out the cause of the problem...
I am making some program that saves some point in a std::map and render those in my window.
But weirdly, some points failed to make it into the map.
std::map<Point2, Prop*> m_Props_m;
void AddProp(std::pair<Point2, Prop*> p)
{
m_Props_m.insert(p);
}
struct Point2
{
unsigned int Point2::x;
unsigned int Point2::y;
//--------
Point2::Point2()
:x(0)
,y(0)
{}
bool Point2::operator< (const Point2& b) const
{
return ( x+y < b.x+b.y );
}
bool Point2::operator> (const Point2& b) const
{
return ( x+y > b.x+b.y );
}
};
Thank god I have some experience with binary trees so I could find out the cause of my problem.
Imagine we have 2 Point2's.
Point2 a(0,1);
Point2 b(1,0);
As you can see, with the operator< method I have written it would return false, and the operator> would also return false. Thus if a is already in the map, and b gets inserted, the insertion fails.
Now, this is all good and well, but how can I fix this? Is there any way I could have a less than operator for a 2D point that would allow me to store every unique point in the map?
std::map doesn't use operator> at all, so you don't have to worry about that.
To sort on multiple fields (in this case, two), use a so-called "lexicographical ordering", meaning that the first field is most important, and the second breaks ties:
bool operator<(const Point2 &lhs, const Point2 &rhs) {
return (lhs.x < rhs.x) || ((lhs.x == rhs.x) && (lhs.y < rhs.y));
}
Your comparison function considers points equivalent if the sum of their coordinates is equal. For example, (2, 5) is equivalent to (3, 4), because 2 + 5 = 3 + 4. Points that already have their equivalent in the map won't make it.
A better idea would be to compare by x first, and by y second if x value is equal in both points.
bool operator< (const Point2 &lhs, const Point2 &rhs) {
return (lhs.x < rhs.x) || ((lhs.x == rhs.x) && (lhs.y < rhs.y));
}
This should do the trick:
bool Point2::operator< (const Point2& b) const
{
if (x<b.x) return true;
else if (!(b.x<x) && y<b.y) return true;
else return false;
}
std::map only uses operator<. If !(a<b) && !(b<a), a and b are equivalent (not the same as equal), and thus the map will only store one of them. operator> is not used.
It might not make sense for comparing Point2's in other cases, so to avoid misunderstandings, I'd suggest that you provide a compare-function for your map, and remove the operator< from your class.
bool mapLessPoint2(const Point2& a, const Point2& b);
std::map<Point2, Prop*, &mapLessPoint2> m_Props_m;
In my opinion operator overloading should only be used when it makes sense for the given type, and that one point in a 2D space is larger than another point in a 2D space is not intuitive.

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.