compare 3 or more objects - c++

Currently, to compare 3 or more integers, We do it this way. (a < b) && (b < c). I know that, a < b < c translates to (a < b) < c and compares boolean with integer. Is there any way such that, I can overload some operators on a custom Class to achieve continuous comparison? How does languages like python does this?
Update: According to accepted answer, I managed to write a piece of code. Have a look.
#include <iostream>
template <typename T>
class Comparator {
bool result;
T last;
public:
Comparator(bool _result, T _last) : result(_result), last(_last) {}
operator bool() const {
return result;
}
Comparator operator<(const T &rhs) const {
return Comparator(result && (last < rhs), rhs);
}
Comparator operator>(const T &rhs) const {
return Comparator(result && (last > rhs), rhs);
}
};
class Int {
int val;
public:
Int(int _val) : val(_val) {}
operator int() const {
return val;
}
Comparator<Int> operator<(const Int &rhs) {
return Comparator<Int>(val < int(rhs), rhs);
}
Comparator<Int> operator>(const Int &rhs) {
return Comparator<Int>(val > int(rhs), rhs);
}
};
int main() {
Int a(2), b(3), c(1), d(4), e(6), f(5);
std::cout << (a < b > c < d < e) << '\n';
// 2 < 3 > 1 < 4 < 6 > 5
return 0;
}

a < b < c is grouped as (a < b) < c.
If a or b are a type that you define, you could overload < for that type to return a proxy object, for which an overloaded < is also defined. That proxy object would contain the value of b along with the result of a < b.
It's some hassle, and will not make your code readable either since all C++ programmers know what a < b < c should do.
Python has its own syntax and interpreter.

Related

incorrect function call on overload operator

In the code, why is (10 != i) calling == instead of !=? The other two call !=
#include <iostream>
class Integer
{
int x;
public:
bool
operator== (const Integer &i)
{
std::cout << "==";
return x == i.x;
}
bool
operator!= (const Integer &i)
{
std::cout << "!=";
return x != i.x;
}
Integer (int t = 0) { x = t; }
};
int
main ()
{
Integer i;
std::cout << (i != i) << '\n'; // calls !=
std::cout << (i != 100) << '\n'; // calls !=
std::cout << (10 != i) << '\n'; // calls ==
}
Prior to C++20, you'd need to add two free functions for the comparison where the int is on the left-hand side:
bool operator==(int lhs, const Integer& rhs) {
return rhs == lhs;
}
bool operator!=(int lhs, const Integer& rhs) {
return rhs != lhs;
}
You should also make the member comparison operators const qualified:
class Integer {
public:
//...
bool operator==(const Integer &i) const { // note const
std::cout << "==";
return x == i.x;
}
bool operator!=(const Integer &i) const { // note const
std::cout << "!=";
return x != i.x;
}
//...
};
You could also remove the member operators to simplify things. Now the left-hand side int will be implicitly converted to Integer and then compared with the right-hand side:
class Integer {
int x;
public:
Integer(int t = 0) : x{t} {}
friend bool operator==(const Integer& lhs, const Integer& rhs) {
return rhs.x == lhs.x;
}
friend bool operator!=(const Integer& lhs, const Integer& rhs) {
return !(rhs == lhs);
}
};
Since you've tagged this C++20, you can let operator== do all the work. See Default comparisons. It'll be used for operator!= too.
class Integer {
int x;
public:
bool operator==(const Integer &i) const {
std::cout << "==";
return x == i.x;
}
Integer(int t = 0) : x{t} {}
};
... and it'll correctly show that it used operator== for all your != comparisons (and negated it).
More from Defaulted equality comparison:
A class can define operator== as defaulted, with a return value of bool. This will generate an equality comparison of each base class and member subobject, in their declaration order. Two objects are equal if the values of their base classes and members are equal. The test will short-circuit if an inequality is found in members or base classes earlier in declaration order.
Per the rules for operator==, this will also allow inequality testing
This means that you will in fact get away with the below only since C++20:
class Integer {
int x;
public:
bool operator==(const Integer &i) const = default;
Integer(int t = 0) : x{t} {}
};
or even better, get all the comparison operators for free by defaulting the spaceship operator <=>:
class Integer {
int x;
public:
auto operator<=>(const Integer &i) const = default;
Integer(int t = 0) : x{t} {}
};
There are two new additions to C++20 that made this possible (note that your code doesn't compile in earlier standard versions).
Compiler will attempt to replace a != b with !(a == b) if there is no suitable != for these arguments.
If there is no suitable a == b, compiler will attempt b == a as well.
So, what happens - compiler first notices that it doesn't know how to compare 10 != i, so it tries !(10 == i). There is still no suitable comparison, so it tries !(i == 10) and it can finally be done using your implicit constructor to convert 10 to Integer.
It can be easily verified by adding more info to debug print:
bool
operator== (const Integer &i) const
{
std::cout << x << "==" << i.x << ' ';
return x == i.x;
}
will print 0==10 1 in the last line (see it online).
As noticed in comments, you don't even need operator !=, due to aforementioned behaviour C++20 compiler will automatically convert any such call to operator ==.

How to use bitfields that make up a sorting key without falling into UB?

Let's say I want to have the following bitfield:
struct SortingKey {
uint8_t a: 2;
uint8_t b: 4;
uint8_t c: 2;
}
To use a simple integer comparison, I may need to wrap it into an union like this and use value for sorting:
union UnionSortKey {
SortingKey key;
uint8_t value;
}
However, in C++, reading an inactive union member is Undefined Behaviour.
How can I guarantee that I do not fall into UB but keeping a simple integer comparison?
You can't use union for type punning,
In C++20, you might use default operator <=>
struct SortingKey {
uint8_t a: 2;
uint8_t b: 4;
uint8_t c: 2;
auto operator <=>(const SortingKey&) const = default;
};
Before, you have to provide the conversion/comparison manually:
bool compare(SortingKey lhs, SortingKey rhs)
{
if (lhs.a != rhs.a) return lhs.a < rhs.a;
if (lhs.b != rhs.b) return lhs.b < rhs.b;
return lhs.c < rhs.c;
}
or
bool compare(SortingKey lhs, SortingKey rhs)
{
auto to_u8 = [](SortingKey s) -> std::uint8_t{ return s.c << 6 | s.b << 2 | s.a; };
return to_u8(lhs) < to_u8(rhs);
}
If you are lucky (bitfield is implementation specific, so...), your compiler might do a simple comparison of underlying type.
(clang succeeds to do that optimization with "correct" order).
or, if you don't have padding bit/byte, you might use memcpy/memcmp (which succeeds to be optimized)
bool compare(SortingKey lhs, SortingKey rhs)
{
auto to_u8 = [](SortingKey s) -> std::uint8_t{
std::uint8_t c; memcpy(&c, &s, 1); return c;
};
return to_u8(lhs) < to_u8(rhs);
}
or
bool compare(SortingKey lhs, SortingKey rhs)
{
return memcmp(&lhs, &rhs, 1) < 0;
}
Demo

bool operator in c++

Here is a snippet of the code that I found on my beginner file:
struct TriIndex //triangle index?
{
int vertex; //vertex
int normal; //normal vecotr
int tcoord; //
bool operator<( const TriIndex& rhs ) const {
if ( vertex == rhs.vertex ) {
if ( normal == rhs.normal ) {
return tcoord < rhs.tcoord;
} else {
return normal < rhs.normal;
}
} else {
return vertex < rhs.vertex;
}
}
};
I've never seen a bool operator inside a struct before. Can anyone explain this to me?
TL;DR: The code inside the function is evaluating if *this is < rhs, bool is merely the return type.
The operator is operator < which is the less than operator. The current object is considered the left hand side or lhs, and the object compared against, the right hand of the a < b expression is rhs.
bool // return type
operator < // the operator
(const TriIndex& rhs) // the parameter
{
...
}
It returns true if the current object is less than (should preceed in containers, etc) the object after the < in an expression like:
if (a < b)
which expands to
if ( a.operator<(b) )
There is a bool operator:
operator bool () const { ... }
which is expected to determine whether the object should evaluate as true:
struct MaybeEven {
int _i;
MaybeEven(int i_) : _i(i_) {}
operator bool () const { return (_i & 1) == 0; }
};
int main() {
MaybeEven first(3), second(4);
if (first) // if ( first.operator bool() )
std::cout << "first is even\n";
if (second) // if ( second.operator bool() )
std::cout << "second is even\n";
}
bool operator<( const TriIndex& rhs )
This line of code is defining a comparison of user-defined datatypes. Here bool is the type of the value this definition will return i.e. true or false.
const TriIndex& rhs
This code is telling the compiler to use struct object as a parameter.
if ( vertex == rhs.vertex ) {
if ( normal == rhs.normal ) {
return tcoord < rhs.tcoord;
} else {
return normal < rhs.normal;
}
} else {
return vertex < rhs.vertex;
}
The above code is defining criteria of the comparison i.e. how the compiler should compare the two when you say struct a < struct b . This is also called Comparison Operator Overloading.
TL-DR;
So after defining the operator when you write a code say:
if (a < b) {
.......
}
where a and b are of type struct fam. Then the compiler will do the if else operations inside the definition and use the return value . If return = true then (a < b) is true otherwise the condition will be false.
That code allows you to compare different TriIndexes with the < operator:
TriIndex alpha;
TriIndex beta;
if (alpha < beta) {
etc;
}
bool operator<( const TriIndex& rhs ) is the "less than" operating (<), bool is the return type of the "less than" operator.
C++ allows you to override operators such as < for structs and classes. For example:
struct MyStruct a, b;
// assign some values to members of a and b
if(a < b) {
// Do something
}
What does this code do? Well it depends on how the "less than" operator has been defined for MyStruct. Basically, when you do a < b it will call the "less than" operator for that struct with b being rhs (right hand side) or whatever you call the first parameter, although rhs is the typical convention.

Ternary comparison operator overloading

How would one implement a ternary comparison operator to determine, for example, the boolean value of a < b < c?
Solution:
When coding a comparison, have the return type be a comparison object that can chain additional comparisons, but is implicitly convertible to a bool. This can even (kind of) work with types that weren't coded with this intent, simply by casting them to the comparison type manually.
Implementation:
template<class T>
class comparison {
const bool result;
const T& last;
public:
comparison(const T& l, bool r=true) :result(r), last(l) {}
operator bool() const {return result;}
comparison operator<(const T& rhs) const {return comparison(rhs, (result && last<rhs));}
comparison operator<=(const T& rhs) const {return comparison(rhs, (result && last<=rhs));}
comparison operator>(const T& rhs) const {return comparison(rhs, (result && last>rhs));}
comparison operator>=(const T& rhs) const {return comparison(rhs, (result && last>=rhs));}
};
A useful example:
#include <iostream>
int main() {
//testing of chained comparisons with int
std::cout << (comparison<int>(0) < 1 < 2) << '\n';
std::cout << (comparison<int>(0) < 1 > 2) << '\n';
std::cout << (comparison<int>(0) > 1 < 2) << '\n';
std::cout << (comparison<int>(0) > 1 > 2) << '\n';
}
Output:
1
0
0
0
Note: This was created by Mooing Duck, and a compiled, more robust example can be found on http://ideone.com/awrmK
Why do you need an operator?
inline bool RangeCheck(int a, int b, int c)
{
return a < b && b < c;
}
or:
#define RANGE_CHECK(a, b, c) (((a) < (b)) && ((b) < (c)))

Implementing operator< in C++

I have a class with a few numeric fields such as:
class Class1 {
int a;
int b;
int c;
public:
// constructor and so on...
bool operator<(const Class1& other) const;
};
I need to use objects of this class as a key in an std::map. I therefore implement operator<. What is the simplest implementation of operator< to use here?
EDIT:
The meaning of < can be assumed so as to guarantee uniqueness as long as any of the fields are unequal.
EDIT 2:
A simplistic implementation:
bool Class1::operator<(const Class1& other) const {
if(a < other.a) return true;
if(a > other.a) return false;
if(b < other.b) return true;
if(b > other.b) return false;
if(c < other.c) return true;
if(c > other.c) return false;
return false;
}
The whole reason behind this post is just that I found the above implementation too verbose. There ought to be something simpler.
I assume you want to implement lexicographical ordering.
Prior to C++11:
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
bool Class1::operator<(const Class1& other) const
{
return boost::tie(a, b, c) < boost::tie(other.a, other.b, other.c);
}
Since C++11:
#include <tuple>
bool Class1::operator<(const Class1& other) const
{
return std::tie(a, b, c) < std::tie(other.a, other.b, other.c);
}
I think there is a misunderstanding on what map requires.
map does not require your class to have operator< defined. It requires a suitable comparison predicate to be passed, which conveniently defaults to std::less<Key> which uses operator< on the Key.
You should not implement operator< to fit your key in the map. You should implement it only if you to define it for this class: ie if it's meaningful.
You could perfectly define a predicate:
struct Compare: std::binary_function<Key,Key,bool>
{
bool operator()(const Key& lhs, const Key& rhs) const { ... }
};
And then:
typedef std::map<Key,Value,Compare> my_map_t;
It depends on if the ordering is important to you in any way. If not, you could just do this:
bool operator<(const Class1& other) const
{
if(a == other.a)
{
if(b == other.b)
{
return c < other.c;
}
else
{
return b < other.b;
}
}
else
{
return a < other.a;
}
}
A version which avoids multiple indentation is
bool operator<(const Class1& other) const
{
if(a != other.a)
{
return a < other.a;
}
if(b != other.b)
{
return b < other.b;
}
return c < other.c;
}
The "Edit 2" version of the author has on average more comparisons than this solution. (worst case 6 to worst case 3)
You could do:
return memcmp (this, &other, sizeof *this) < 0;
but that has quite a lot of of caveats - no vtbl for example and plenty more I'm sure.