Using a (mathematical) vector in a std::map - c++

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.

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);
}

C++ set with arbitrary comparator

I have the following C++ code
#include <set>
#include <string>
#include <iostream>
using namespace std;
class Pair {
public:
string lhs;
string rhs;
Pair();
Pair( string l, string r ) {
lhs=l;
rhs=r;
};
};
struct compare {
bool operator()(const Pair& a, const Pair& b) const{
if ( ( a.lhs == b.lhs && a.rhs == b.rhs ) || ( a.lhs == b.rhs && a.rhs == b.lhs ) ) {
cout << "MATCH" << endl;
}
return ( a.lhs == b.lhs && a.rhs == b.rhs ) || ( a.lhs == b.rhs && a.rhs == b.lhs );
}
};
int main () {
set<Pair, compare > s;
Pair p( string("Hello"), string("World") );
s.insert(p);
cout << s.size() << "\n";
Pair q( string("World"), string("Hello") );
s.insert(q);
cout << s.size() << "\n";
compare cmp;
cout << cmp( p, q );
return 0;
}
Invoking the compiled code gives:
1
MATCH
MATCH
2
MATCH
Somehow the set s ends up with both Pairs p, and q in spite of the fact that the comparator identifies them as identical.
Why?
Any help will be much appreciated!
UPDATE:
Many thanks for the great answers and your kind and professional help.
As you might have guessed already, I am quite a newby to C++.
Anyway, I was wondering, if Antoine's answer could be done with a lambda expression?
Something like:
std::set< …, [](){ my_comparator_code_here } > s;
????
The comparison operator for a std::set (which is an ordered container) needs to identify a strict weak ordering not any arbitrary test you wish. Normally a properly implemented operator< does the job.
If your comparison operator does not provide a strict weak ordered (as yours does not) the behavior will be undefined. There is no way to work around this requirement of the C++ standard.
Note that in certain cases where an equality comparison is needed it will have to use the operator< twice to make the comparison.
Also have you considered using std::pair<std::string, std::string> instead of rolling your own?
I've reread your question about five times now and I'm starting to wonder if what you want is a set of pairs where which string is in first and second doesn't matter as far as the comparison goes. In that case #Antoine has what appears to be the correct solution for you.
A comparator for a set, map or any algorithm such as lower_bound or sort which require an order need to implement a strict weak ordering (basically, behave like a <).
Such an ordering is required to have 3 properties:
irreflexive: not (a < a) is always true
asymmetric: a < b implies not (b < a)
transitive: a < b and b < c imply a < c
Which you will not < has.
Such an ordering defines equivalence classes, which are groups of elements that compare equal according to the ordering (that is not (a < b) and not (b < a) is verified). In a set or map, only a single element per equivalence class can be inserted whereas a multiset or multimap may hold multiple elements per equivalence class.
Now, if you look at your comparator, you will realize that you have implemented == which does not define any order at all. You need to implement something akin to < instead.
A simple, but extremely efficient trick, is to use tuples which have < (and == and any other comparison operator) already implemented in a lexicographical order. Thus, std::tuple<std::string, std::string> has exactly the order you which; and even better, std::tuple<std::string const&, std::string const&> also has it, and can be constructed very easily using std::tie.
Therefore, the implementation of a straightforward comparator is as simple as:
struct comparator {
bool operator()(Pair const& left, Pair const& right) const {
return std::tie( left.a, left.b)
< std::tie(right.a, right.b);
}
};
Note: although not discussed much, it is absolutely essential that the ordering of the comparator be stable across calls. As such, it should generally only depend on the values of the elements, and nothing external or runtime-related (such as their addresses in memory)
EDIT: as noted, your comparator is slightly more complicated.
In your case, though, you also need to take into account that a and b have a symmetric role. In general, I would suggest uniquifying the representation in the constructor of the object; if not possible, you can uniquify first and compare second:
struct comparator {
bool operator()(Pair const& left, Pair const& right) const {
auto uleft = left.a < left.b ? std::tie(left.a, left.b)
: std::tie(left.b, left.a);
auto uright = right.a < right.b ? std::tie(right.a, right.b)
: std::tie(right.b, right.a);
assert(get<0>(uleft) <= get<1>(uleft) and "Incorrect uleft");
assert(get<0>(uright) <= get<1>(uright) and "Incorrect uright");
return uleft < uright;
}
}; // struct comparator
As Mark B said compare represents an ordering and not an equality, by default it is std::less. In your case, you don't want the comparison to depend on the order in your pair, but at the same time, your operator< must be satisfy a number of conditions.
All the answers here propose to change your specification and make the comparison order-dependant. But if you don't want that, here is the solution:
bool operator()(const Pair & a, const Pair & b) {
const bool swapA = a.lhs < a.rhs;
const std::string & al = swapA ? a.lhs : a.rhs;
const std::string & ar = swapA ? a.rhs : a.lhs;
const bool swapB = b.lhs < b.rhs;
const std::string & bl = swapB ? b.lhs : b.rhs;
const std::string & br = swapB ? b.rhs : b.lhs;
return al < bl || (al == bl && ar < br);
}
At least, it works on your example, and the relation is reflexive and transitive.
Here is how it works: it is the lexicographic order for pairs: al < bl || (al == bl && ar < br), applied to sorted pairs.
In fact your data structure is a (set of size N) of (set of size 2). Internally, std::set sorts its elements using your comparison operators. For your "set of size 2" Pair you also need to consider them as internally sorted.
If the comparison code looks too heavy, you could move the pair sorting into the Pair class, like implement two methods min() and max(). Also, you implement operator< and then don't need a compare class:
struct Pair {
string lhs, rhs;
Pair();
Pair( string l, string r ) : lhs(l), rhs(r) {}
const std::string & min() const { return lhs < rhs ? lhs : rhs; }
const std::string & max() const { return lhs < rhs ? rhs : lhs; }
bool operator<(const Pair& b) const {
return min() < b.min() || (min() == b.min() && max() < b.max());
}
};
from here
The set object uses this expression to determine both the order the elements follow in the container and whether two element keys are equivalent (by comparing them reflexively: they are equivalent if !comp(a,b) && !comp(b,a)). No two elements in a set container can be equivalent.
Sorry all jumped the gun becuase I disliked another answer. I will exapand and correct momentarily. AS pointed out, an order needs to be implemented. typcially this would be a lexicographical order. Importantly however you still need to make sure that the case for which you consider two pairs to be equal returns false for both cases.
if (( a.lhs == b.lhs && a.rhs == b.rhs ) || ( a.lhs == b.rhs && a.rhs == b.lhs )) return false;
//ordinary lexicographical compare
if( a.lhs < b.lhs) return true;
else if( a.lhs == b.lhs && a.rhs < b.rhs) return true;
else return false;
Notic the "!", simple. Your code is saying pair one is less than pair two which is less than pair one. You want it to say that neither is less than the other.
DISCLAIMER STILL WRONG ON A TECHNICALITY, ANTOINE'S IS THE CORRECT ONE

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.

What requirements must std::map key classes meet to be valid keys?

I want to map objects of a given class to objects of another. The class I want to use as key, however, was not written by me and is a simple struct with a few values. std::map orders it's contents, and I was wondering how it does it, and if any arbitrary class can be used as a key or if there's a set of requirements (operators and what not) that need to be defined.
If so, I could create a wrapper for the class implementing the operators map uses. I just need to know what I need to implement first, and none of the references for the class I found online specify them.
All that is required of the key is that it be copiable and assignable.
The ordering within the map is defined by the third argument to the
template (and the argument to the constructor, if used). This
defaults to std::less<KeyType>, which defaults to the < operator,
but there's no requirement to use the defaults. Just write a comparison
operator (preferably as a functional object):
struct CmpMyType
{
bool operator()( MyType const& lhs, MyType const& rhs ) const
{
// ...
}
};
Note that it must define a strict ordering, i.e. if CmpMyType()( a, b
) returns true, then CmpMyType()( b, a ) must return false, and if
both return false, the elements are considered equal (members of the
same equivalence class).
You need to define the operator<, for example like this :
struct A
{
int a;
std::string b;
};
// Simple but wrong as it does not provide the strict weak ordering.
// As A(5,"a") and A(5,"b") would be considered equal using this function.
bool operator<(const A& l, const A& r )
{
return ( l.a < r.a ) && ( l.b < r.b );
}
// Better brute force.
bool operator<(const A& l, const A& r )
{
if ( l.a < r.a ) return true;
if ( l.a > r.a ) return false;
// a are equal, compare b
return ( l.b < r.b );
}
// This can often be seen written as
bool operator<(const A& l, const A& r )
{
// This is fine for a small number of members.
// But I prefer the brute force approach when you start to get lots of members.
return ( l.a < r.a ) ||
(( l.a == r.a) && ( l.b < r.b ));
}
The answer is actually in the reference you link, under the description of the "Compare" template argument.
The only requirement is that Compare (which defaults to less<Key>, which defaults to using operator< to compare keys) must be a "strict weak ordering".
Same as for set: The class must have a strict ordering in the spirit of "less than". Either overload an appropriate operator<, or provide a custom predicate. Any two objects a and b for which !(a<b) && !(b>a) will be considered equal.
The map container will actually keep all the elements in the order provided by that ordering, which is how you can achieve O(log n) lookup and insertion time by key value.