How to define operator< on n-tuple (for example on 3-tuple) so that it satisfy strict weak ordering concept ? I know that boost library has tuple class with correctly defined operator< but for some reasons I can't use it.
strict weak ordering
This is a mathematical term to define a relationship between two objects.
Its definition is:
Two objects x and y are equivalent if both f(x, y) and f(y, x) are false. Note that an object is always (by the irreflexivity invariant) equivalent to itself.
In terms of C++ this means if you have two objects of a given type, you should return the following values when compared with the operator <.
X a;
X b;
Condition: Test: Result
a is equivalent to b: a < b false
a is equivalent to b b < a false
a is less than b a < b true
a is less than b b < a false
b is less than a a < b false
b is less than a b < a true
How you define equivalent/less is totally dependent on the type of your object.
Formal Definition:
Strict Weak ordering
Computer Science:
Strict Weak Ordering
How it relates to operators:
Comparator
As a side note we can implement strict weak ordering manually. But we can do it simply using the std::tuple which has implemented it for you. You simply need to create a tuple without copying the objects.
struct S
{
ThingA a;
ThingB b;
};
bool operator<(S const& lhs, S const& rhs)
{
return std::tie(lhs.a, lhs.b) < std::tie(rhs.a, rhs.b);
}
Note: This assumes that thingA and thingB already implement strict weak ordering themselves.
We can also implement equality the same way:
bool operator==(S const& lhs, S const& rhs)
{
return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
}
Note again: This assumes that thingA and thingB already implement equality.
if (a1 < b1)
return true;
if (b1 < a1)
return false;
// a1==b1: continue with element 2
if (a2 < b2)
return true;
if (b2 < a2)
return false;
// a2 == b2: continue with element 3
if (a3 < b3)
return true;
return false; // early out
This orders the elements by a1 being most siginificant and a3 least significant.
This can be continued ad infinitum, you could also e.g. apply it to a vector of T, iterating over comparisons of a[i] < a[i+1] / a[i+1] < a[i]. An alternate expression of the algorithm would be "skip while equal, then compare":
while (i<count-1 && !(a[i] < a[i+1]) && !(a[i+1] < a[i])
++i;
return i < count-1 && a[i] < a[i+1];
Of course, if the comparison is expensive, you might want to cache the comparison result.
[edit] removed wrong code
[edit] if more than just operator< is available, I tend to use the pattern
if (a1 != b1)
return a1 < b1;
if (a2 != b2)
return a2 < b2;
...
...a new answer to a very old question, but the existing answer miss the easy solution from C++11...
C++11 solution
C++11 onwards provides std::tuple<T...>, which you can use to store your data. tuples have a matching operator< that initially compares the left-most element, then works along the tuple until the outcome's clear. That's suitable for providing the strict weak ordering expected by e.g. std::set and std::map.
If you have data in some other variables (e.g. fields in a struct), you can even use std::tie() to creates a tuple of references, which can then be compared to another such tuple. That makes it easy to write operator< for specific member-data fields in a user-defined class/struct type:
struct My_Struct
{
int a_;
double b_;
std::string c_;
};
bool operator<(const My_Struct& lhs, const My_Struct& rhs)
{
return std::tie(lhs.a_, lhs.b_, lhs.c_) < std::tie(rhs.a_, rhs.b_, rhs.c_);
}
You could simply use three-element vectors, which will already have operator<() suitably
defined. This has the advantage that it extends to N-elements without you having to do anything.
The basic flow should be along the lines of: if the Kth elements are different, return which is smaller else go to next element. The code following assumes you don't have a boost tuple otherwise you would use get<N>(tuple) and not have the problem to begin with.
if (lhs.first != rhs.first)
return lhs.first < rhs.first;
if (lhs.second != rhs.second)
return lhs.second< rhs.second;
return lhs.third < rhs.third;
Even if you can't use the boost version, you should be able to nick the code. I nicked this from std::pair - a 3 tuple will be similar I guess.
return (_Left.first < _Right.first ||
!(_Right.first < _Left.first) && _Left.second < _Right.second);
Edit: As a couple of people have pointed out, if you steal code from the standard library to use in your code, you should rename things that have underscores on the front as these names are reserved.
Note that interestingly, an operator < that always returns false meets the requirements of strict weak ordering.
Related
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);
}
How to define operator< on n-tuple (for example on 3-tuple) so that it satisfy strict weak ordering concept ? I know that boost library has tuple class with correctly defined operator< but for some reasons I can't use it.
strict weak ordering
This is a mathematical term to define a relationship between two objects.
Its definition is:
Two objects x and y are equivalent if both f(x, y) and f(y, x) are false. Note that an object is always (by the irreflexivity invariant) equivalent to itself.
In terms of C++ this means if you have two objects of a given type, you should return the following values when compared with the operator <.
X a;
X b;
Condition: Test: Result
a is equivalent to b: a < b false
a is equivalent to b b < a false
a is less than b a < b true
a is less than b b < a false
b is less than a a < b false
b is less than a b < a true
How you define equivalent/less is totally dependent on the type of your object.
Formal Definition:
Strict Weak ordering
Computer Science:
Strict Weak Ordering
How it relates to operators:
Comparator
As a side note we can implement strict weak ordering manually. But we can do it simply using the std::tuple which has implemented it for you. You simply need to create a tuple without copying the objects.
struct S
{
ThingA a;
ThingB b;
};
bool operator<(S const& lhs, S const& rhs)
{
return std::tie(lhs.a, lhs.b) < std::tie(rhs.a, rhs.b);
}
Note: This assumes that thingA and thingB already implement strict weak ordering themselves.
We can also implement equality the same way:
bool operator==(S const& lhs, S const& rhs)
{
return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
}
Note again: This assumes that thingA and thingB already implement equality.
if (a1 < b1)
return true;
if (b1 < a1)
return false;
// a1==b1: continue with element 2
if (a2 < b2)
return true;
if (b2 < a2)
return false;
// a2 == b2: continue with element 3
if (a3 < b3)
return true;
return false; // early out
This orders the elements by a1 being most siginificant and a3 least significant.
This can be continued ad infinitum, you could also e.g. apply it to a vector of T, iterating over comparisons of a[i] < a[i+1] / a[i+1] < a[i]. An alternate expression of the algorithm would be "skip while equal, then compare":
while (i<count-1 && !(a[i] < a[i+1]) && !(a[i+1] < a[i])
++i;
return i < count-1 && a[i] < a[i+1];
Of course, if the comparison is expensive, you might want to cache the comparison result.
[edit] removed wrong code
[edit] if more than just operator< is available, I tend to use the pattern
if (a1 != b1)
return a1 < b1;
if (a2 != b2)
return a2 < b2;
...
...a new answer to a very old question, but the existing answer miss the easy solution from C++11...
C++11 solution
C++11 onwards provides std::tuple<T...>, which you can use to store your data. tuples have a matching operator< that initially compares the left-most element, then works along the tuple until the outcome's clear. That's suitable for providing the strict weak ordering expected by e.g. std::set and std::map.
If you have data in some other variables (e.g. fields in a struct), you can even use std::tie() to creates a tuple of references, which can then be compared to another such tuple. That makes it easy to write operator< for specific member-data fields in a user-defined class/struct type:
struct My_Struct
{
int a_;
double b_;
std::string c_;
};
bool operator<(const My_Struct& lhs, const My_Struct& rhs)
{
return std::tie(lhs.a_, lhs.b_, lhs.c_) < std::tie(rhs.a_, rhs.b_, rhs.c_);
}
You could simply use three-element vectors, which will already have operator<() suitably
defined. This has the advantage that it extends to N-elements without you having to do anything.
The basic flow should be along the lines of: if the Kth elements are different, return which is smaller else go to next element. The code following assumes you don't have a boost tuple otherwise you would use get<N>(tuple) and not have the problem to begin with.
if (lhs.first != rhs.first)
return lhs.first < rhs.first;
if (lhs.second != rhs.second)
return lhs.second< rhs.second;
return lhs.third < rhs.third;
Even if you can't use the boost version, you should be able to nick the code. I nicked this from std::pair - a 3 tuple will be similar I guess.
return (_Left.first < _Right.first ||
!(_Right.first < _Left.first) && _Left.second < _Right.second);
Edit: As a couple of people have pointed out, if you steal code from the standard library to use in your code, you should rename things that have underscores on the front as these names are reserved.
Note that interestingly, an operator < that always returns false meets the requirements of strict weak ordering.
struct Object
{
int x;
int y;
bool isXValid()
{
return x > 0;
}
};
bool mySort(const Object& lhs, const Object& rhs)
{
// Note the short-circuit here
bool isValidForCheck = lhs.isXValid() && rhs.isXValid();
// rhs may be valid because short-circuit, or both may be invalid
if (isValidForCheck)
{
return lhs.x < rhs.x;
}
return lhs.y < rhs.y;
}
int main()
{
std::vector<Object> myVec;
// random populating of myVec
std::sort(myVec.begin(), myVec.end(), mySort);
return 0;
}
My comparison function is irreflexive but does not respect transitivity.
stl ordering - strict weak ordering
Thus, if:
(a < b) == true, (b < a) must be false.
Additionally, if:
a == b, then (a < b) and (b < a) are false.
However, it is not transitive. For example, with the following values:
(1, 3) (-1, 2), (3, 1)
Then it is possible to produce a sorted container where:
a < b, and b < c, but c < a.
This answer : What causes std::sort() to access address out of range explains why std::sort will address out-of-bounds when the comparison function doesn't respect irreflexibility (for example, quicksort where we do comp(pivot, pivot) and get true, thus the left pointer keeps walking right off the end of the array).
However, I am seeing crashes with comparisons that are irreflexive but non-transitive. I also believe I am going off the end of the array.
I cannot provide the exact code here as it is proprietary. The example above will not crash in my tests, but I am aware that std::sort will select different algorithms based upon the container and elements to be sorted.
What I'd like to know is if anybody knows why std::sort would crash under GNU C++ when the comparison is non-transitive? I know that non-transitive comparisons break the contract and it's undefined behaviour, but I was hoping for a similar answer to the one in the linked question - i.e. precisely why it can crash.
How to define operator< on n-tuple (for example on 3-tuple) so that it satisfy strict weak ordering concept ? I know that boost library has tuple class with correctly defined operator< but for some reasons I can't use it.
strict weak ordering
This is a mathematical term to define a relationship between two objects.
Its definition is:
Two objects x and y are equivalent if both f(x, y) and f(y, x) are false. Note that an object is always (by the irreflexivity invariant) equivalent to itself.
In terms of C++ this means if you have two objects of a given type, you should return the following values when compared with the operator <.
X a;
X b;
Condition: Test: Result
a is equivalent to b: a < b false
a is equivalent to b b < a false
a is less than b a < b true
a is less than b b < a false
b is less than a a < b false
b is less than a b < a true
How you define equivalent/less is totally dependent on the type of your object.
Formal Definition:
Strict Weak ordering
Computer Science:
Strict Weak Ordering
How it relates to operators:
Comparator
As a side note we can implement strict weak ordering manually. But we can do it simply using the std::tuple which has implemented it for you. You simply need to create a tuple without copying the objects.
struct S
{
ThingA a;
ThingB b;
};
bool operator<(S const& lhs, S const& rhs)
{
return std::tie(lhs.a, lhs.b) < std::tie(rhs.a, rhs.b);
}
Note: This assumes that thingA and thingB already implement strict weak ordering themselves.
We can also implement equality the same way:
bool operator==(S const& lhs, S const& rhs)
{
return std::tie(lhs.a, lhs.b) == std::tie(rhs.a, rhs.b);
}
Note again: This assumes that thingA and thingB already implement equality.
if (a1 < b1)
return true;
if (b1 < a1)
return false;
// a1==b1: continue with element 2
if (a2 < b2)
return true;
if (b2 < a2)
return false;
// a2 == b2: continue with element 3
if (a3 < b3)
return true;
return false; // early out
This orders the elements by a1 being most siginificant and a3 least significant.
This can be continued ad infinitum, you could also e.g. apply it to a vector of T, iterating over comparisons of a[i] < a[i+1] / a[i+1] < a[i]. An alternate expression of the algorithm would be "skip while equal, then compare":
while (i<count-1 && !(a[i] < a[i+1]) && !(a[i+1] < a[i])
++i;
return i < count-1 && a[i] < a[i+1];
Of course, if the comparison is expensive, you might want to cache the comparison result.
[edit] removed wrong code
[edit] if more than just operator< is available, I tend to use the pattern
if (a1 != b1)
return a1 < b1;
if (a2 != b2)
return a2 < b2;
...
...a new answer to a very old question, but the existing answer miss the easy solution from C++11...
C++11 solution
C++11 onwards provides std::tuple<T...>, which you can use to store your data. tuples have a matching operator< that initially compares the left-most element, then works along the tuple until the outcome's clear. That's suitable for providing the strict weak ordering expected by e.g. std::set and std::map.
If you have data in some other variables (e.g. fields in a struct), you can even use std::tie() to creates a tuple of references, which can then be compared to another such tuple. That makes it easy to write operator< for specific member-data fields in a user-defined class/struct type:
struct My_Struct
{
int a_;
double b_;
std::string c_;
};
bool operator<(const My_Struct& lhs, const My_Struct& rhs)
{
return std::tie(lhs.a_, lhs.b_, lhs.c_) < std::tie(rhs.a_, rhs.b_, rhs.c_);
}
You could simply use three-element vectors, which will already have operator<() suitably
defined. This has the advantage that it extends to N-elements without you having to do anything.
The basic flow should be along the lines of: if the Kth elements are different, return which is smaller else go to next element. The code following assumes you don't have a boost tuple otherwise you would use get<N>(tuple) and not have the problem to begin with.
if (lhs.first != rhs.first)
return lhs.first < rhs.first;
if (lhs.second != rhs.second)
return lhs.second< rhs.second;
return lhs.third < rhs.third;
Even if you can't use the boost version, you should be able to nick the code. I nicked this from std::pair - a 3 tuple will be similar I guess.
return (_Left.first < _Right.first ||
!(_Right.first < _Left.first) && _Left.second < _Right.second);
Edit: As a couple of people have pointed out, if you steal code from the standard library to use in your code, you should rename things that have underscores on the front as these names are reserved.
Note that interestingly, an operator < that always returns false meets the requirements of strict weak ordering.
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