generate == operator that uses non-defaulted <=> - c++

In a slight variation of this question. I would like to define a custom type with a custom <=> operator and use that custom <=> operator to generate a ==. Trying the following
#include <compare>
#include <iostream>
#include <cassert>
struct widget {
int member;
int non_comparison_data;
friend std::strong_ordering operator<=>(const widget& lhs,
const widget& rhs) {
std::cout << "doing a three way comparison" << std::endl;
return lhs.member <=> rhs.member;
}
// friend bool operator==(const widget& lhs, const widget& rhs) {
// return 0 == (lhs <=> rhs);
// }
friend bool operator==(const widget& lhs, const widget& rhs) = default;
};
int main() {
widget a{.member = 1, .non_comparison_data = 23};
widget b{.member = 1, .non_comparison_data = 42};
assert(a==b);
return 0;
}
I observe that a default == operator does not use the custom <=> operator, but instead does what would be done in absence of any custom comparison and compares all data members. I can define an operator == on my own that uses <=> like follows, but I'm wondering if there's a better way to get == out of <=>.
friend bool operator==(const widget& lhs, const widget& rhs) {{
return 0 == (lhs <=> rhs);
}
PS: compiler-explorer link
PS: I'm aware a custom <=> doesn't generate a default == because == is likely implementable in a more optimal way than using <=> and one does not want an inefficient default to be generated.

I'm wondering if there's a better way to get == out of <=>.
Nope, there's not. What you did (return 0 == (lhs<=>rhs);) is the optimal way. What more were you looking for?

The only way to define == in terms of <=> is to do it manually. Which is what you're already doing.
You can do a little bit better by writing member functions (the only benefit of non-member friends would be if you wanted to take by value. But if you're not doing that, member functions are easier - simply less code to write). And then implement == forwards instead of backwards... no Yoda conditionals please.
struct widget {
int member;
int non_comparison_data;
std::strong_ordering operator<=>(const widget& rhs) const {
return member <=> rhs.member;
}
bool operator==(const widget& rhs) const {
return (*this <=> rhs) == 0;
}
};

Related

overwrite c++20 spaceship operator for enum class

I'm struggling with providing the new spaceship-operator for a enum class. Lets take the following example:
#include <cstdio>
#include <iostream>
#include <compare>
#include <cstdint>
enum class Animals : uint8_t
{
Bird = 27, //those values are just for making a point
Tiger = 5,
Ant = 100,
Snake = 45,
Wale = 17
};
//auto operator<=(const Animals& lhs, const Animals& rhs) = delete;
//auto operator>=(const Animals& lhs, const Animals& rhs) = delete;
//auto operator<(const Animals& lhs, const Animals& rhs) = delete;
//auto operator>(const Animals& lhs, const Animals& rhs) = delete;
auto operator<=>(const Animals& lhs, const Animals& rhs)
{
std::cout << "comparing via overloaded <=> operator\n";
//order the animals by their size in real life
// Ant < Bird < Snake < Tiger < Wale
//for this MVCE only Ant < Tiger is in here:
if(lhs == Animals::Ant && rhs == Animals::Tiger)
return -1;
return 0; //all unimplemented ones are treated as equal
}
int main(void)
{
if(Animals::Ant < Animals::Tiger)
std::puts("success (do I see the prompt of the overloaded operator?)");
else
std::puts("seems to use uint8_t comparison instead");
return 0;
}
But obviously I'm getting something wrong in here, as my main() still tells me, that Ants are bigger than Tigers. As you can see I tried to explicitly delete the default comparison-operators, to force the compiler to use my custom-spaceship-one, but without success.
When I explicit call auto result = Animals::Ant <=> Animals::Tiger I get an ambiguous overload for 'operator<=>' (operand types are 'Animals' and 'Animals'). But that seems to be related to my operators signature (using const Animals instead).
Is it possible to overwrite the operator for my Enum (without interfering with the operators for its basic type "uint8_t"?
There are two things wrong with your operator<=>:
it needs to take the enums by value, not by reference-to-const, in order to the suppress the built-in candidate
it needs to return one of the comparison categories, not int. In this case probably std::weak_ordering is right.
That is:
constexpr auto operator<=>(Animals lhs, Animals rhs) -> std::weak_ordering
{
//order the animals by their size in real life
// Ant < Bird < Snake < Tiger < Wale
//for this MVCE only Ant < Tiger is in here:
if(lhs == Animals::Ant && rhs == Animals::Tiger)
return std::weak_ordering::less;
return std::weak_ordering::equivalent;
}
That said, there is implementation divergence for how to handle rewrite candidates with <=>. clang and msvc implement what the rules probably should be, which is that our user-declared operator<=> suppresses all the built-in relational and three-way comaprison operators, so that Animals::Ant < Animals::Tiger invokes our operator<=>. But gcc implements what the rules technically actually literally say, which is that Animals::Ant <=> Animals::Tiger evaluates our operator but using < does not. There is a gcc bug report open for this (#105200), where one of the gcc developers points out the wording issue. This strikes me as a wording issue, rather than an actual design intent issue, so I'm opening a Core issue about this (#205).
In order for this to work on gcc, you have to also go through and add these yourself (note: always by value):
constexpr auto operator<(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) < 0;
}
constexpr auto operator<=(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) <= 0;
}
constexpr auto operator>(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) > 0;
}
constexpr auto operator>=(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) >= 0;
}

boost operator totally_ordered composed of less_than_comparable and equality_comparable

As boost operator document say,template totally_ordered is composed of template less_than_comparable and tempalte equality_comparable.
It means that if a class inherents from template totally_ordered, operator== must be implemented when using operator== or operator!=.
In my view, if operator< is implemented, operator== can be generated automatically like (!(lhs < rhs) && !(rhs < lhs)).So, is operator== necessary ?
code piece:
#include <boost/operators.hpp>
class Foo : public boost::totally_ordered<Foo>
{
public:
explicit Foo(const int num) : m_nMem(num){}
friend bool operator< (const Foo& lhs, const Foo& rhs)
{
return lhs.m_nMem < rhs.m_nMem;
}
// Is operator== necessary ?
// Is operator== equal to (!(lhs < rhs) && !(rhs < lhs)) ?
//friend bool operator== (const Foo& lhs, const Foo& rhs)
//{
// return lhs.m_nMem == rhs.m_nMem;
//}
private:
int m_nMem;
};
int main()
{
Foo foo_1(1), foo_2(2);
foo_1 == foo_2; // compiler error , no operator==
return 0;
}
A strict weak ordering may rate unequal elements equivalent¹
E.g.:
struct Point {
int x,y;
bool operator<(Point const& other) const { return x < other.x; }
};
Here, Points would be ordered by x, and points having equal x would be equivalent according to your suggested implementation.
However, since y may be different, clearly the points are not guaranteed to be equal.
Only if the comparison is in fact a total ordering, then we can generate the equality operation using the relative comparison operators. I can only suspect the library authors
wanted the users to be very conscious of this implications
realized that using (!(lhs < rhs) && !(rhs < lhs)) might lead to suboptimal performance
¹ https://www.sgi.com/tech/stl/StrictWeakOrdering.html

Can I compare 2 structures in C++?

I have simply declared a structure like this -
struct data{
int x,y;
};
Now I have declared 2 variables a & b of data type. I've assigned appropriate values to them. Now, I want to check if they are equal! I am trying to do like this -
data a,b;
a.x=12, a.y=24;
b.x=15, b.y=30;
if(a!=b)cout<<"a~b"<<endl;
But the compiler is giving me the following error on the 4th line ->
error: no match for 'operator!=' (operand types are 'data' and 'data')
Where is the problem actually? Isn't this compare supported in C++?? Or I'm making any mistakes??
What is the exact and easiest way to do this?? Do I need to compare each of the elements in the structure separately?? Or there's any other smarter way??
C++ gives you attribute-by-attribute assignment implicitly, but no comparison for equality or ordering. The reason is "just because", don't look too hard into philosophy.
You must to provide those operators, if needed, by implementing them yourself explicitly, for example:
bool operator<(const Data& other) const {
if (x < other.x) return true;
if (x > other.x) return false;
return y < other.y;
}
bool operator==(const Data& other) const {
return x == other.x && y == other.y;
}
and so on.
Note also that defining for example == doesn't give you != automatically and defining < doesn't provide >= implicitly.
UPDATE
C++20 introduces (will introduce) a new operator <=> (friendly name "spaceship operator") exactly to remove the verbosity of having to define all possible relational operators. In this case adding:
std::strong_ordering operator<=>(const Data& other) const {
if (auto cmp = x <=> other.x; cmp != 0) return cmp;
return y <=> other.y;
}
will allow compilation of all relational tests (<, <=, >, >=, ==, !=) between elements of the class based on checking x first and, if that check doesn't resolve, checking y instead.
You have to implement all operators explicitely that you intent to use. In your case, you will need to supply bool operator!=(const data&, const data&).
A nice way to implement it for PODs like this is to use std::tuple since it already implements ordering:
#include <tuple>
// ...
bool operator!=(const data& p_lhs, const data& p_rhs)
{
return std::tie(p_lhs.x, p_lhs.y) != std::tie(p_rhs.x, p_rhs.y);
}
std::tie (documentation) creates a temporary tuple of references. Those two tuples can then be compared, since std::tuple defines all comparison operators, as shown here.
I chose to implement operator!= as a free function. You can, of course, choose to implement it as member of your class:
struct data
{
bool operator!=(const data& p_rhs) const
{
return std::tie(x, y) != std::tie(p_rhs.x, p_rhs.y);
}
int x, y;
};
Of course you should define all other operators, too. Remember that you can implement most operators by delegating to others.
Automatic C++ comparisons are coming in C++20, so you can just add a special operator to indicate that you need default comparisons when new standard is out.
class Point {
int x;
int y;
public:
auto operator<=>(const Point&) const = default;
// ... non-comparison functions ...
};
https://en.cppreference.com/w/cpp/language/default_comparisons
You have to implement bool operator != (const data&, const data&);.
Possible implementation (in c++11):
#include <tuple>
//...
bool operator == (const data& lhs, const data& rhs) {
return std::tie(lhs.x, lhs.y) == std::tie(rhs.x, rhs.y);
}
bool operator != (const data& lhs, const data& rhs) {
return !(lhs == rhs);
}

How to overload operator ==?

I have class A
how to overload operator == to perform
A a,b,c;
if (a==b==c) {}
Could anyone help me?
Very short answer: NO!
Slightly longer answer: Don't try this.
Explanation: Every C++ programmer is used to have the comparison operator return a bool or something convertible to bool. Just because it's natural to type things like if (a==b).
So if the expression a==b returns a bool x, then a==b==c would mean comparing x with c. Which makes no sense at all. Even if you get it to compile, e.g. comparing ints that way, it would not yield the result you expect.
So, while there technically I can think of a solution to what you seem to want (compare if all three are equal), the right thing to do is how it is always done in C++: logically chain binary comparisons:
if (a==b && b==c) {}
for the ones who wonder about the "technical doable but oh so ugly" solution:
(DON'T DO THIS IN REAL WORLD USE! You should get fired if you do.)
template <class T>
struct multiCompareProxy {
bool b;
T const& t;
explicit operator bool() const {return b;}
multiCompareProxy operator==(T const& rhs) {
return {b && t.equals(rhs), t};
}
};
template <class T>
multiCompareProxy<T> operator==(T const& lhs, T const& rhs) {
return {lhs.equals(rhs), lhs};
}
A class now just has to overload the euqals method for this to work: Example
If for whatever reason you really wanted to do this, you'd need a proxy object like so:
struct A {};
struct Proxy {};
Proxy operator==(const A& a, const A& b) {
return {};
}
bool operator==(const Proxy& p, const A& b) {
return true;
}
bool operator==(const A& a, const Proxy& p) {
return true;
}
#include <iostream>
int main() {
A a, b, c;
if(a == b == c) {
std::cout << "Bad.\n";
}
}
But don't do this. Use (a == b) && (a == c) like everyone else.
It can be done, and it's very occasionally useful as a kind of domain-specific language (DSL) for matching the notation expected by non-C++-programmers, but it should be studiously avoided for other uses as it'll confound (and annoy) programmers working on the code if this is used willy-nilly.
class A
{
// ...
bool equals(const A& rhs) const { return ... }
struct X
{
X(const A& a, bool b) : a_(a), b_(b) { }
X& operator==(const A& rhs) const { b_ &= a_.equals(rhs); return *this; }
explicit operator bool() const { return b_; }
// remove explicit pre C++11 / consider making it operator void*() ...
const A& a_;
mutable bool b_;
};
X A::operator==(const A& rhs) const
{
return X(*this, equals(rhs));
}
};
(You might prefer standalone functions if you have implicit constructors).
The same kind of proxy-using hackery allows all manner of unexpected notations like 3 < x < 9 and x == a1 || a2 || a3... again, avoid them unless it'll make a huge difference to the maintainers of the code where they'll be used (and ideally that code will have very clear boundaries from the other C++ code in your system).
As an example of good uses of such techniques, consider the boost spirit library and it's unusual use of a lot of operators to provide something approximating BNF notation....
You cannot (minus the possibility of an ugly hack) overload operator==(...) to work as you have specified.
If you think about what it would do a == b would become either true or false (a bool), then you would have (<bool> == c) left. The proper way to do what you are looking to do would be some form of (a==b) && (b==c).
There are times when you can overload an operator to work as you describe, but it would have to return the same type that it takes. An example would be:
class A
{
A& operator+=(A const& rhs)
{
// operator logic
return *this;
}
}
This case works because with a += b += c, you would perform (b += c) which would return an A&, which the remaining a.operator+=(...) accepts as an argument.
bool operator==(T const & a, T const & b) {
return /*boolean expr*/
}
if you have a class you can do:
class MyClass {
public:
bool operator==(T const & rhs) const {
return /*boolean expr*/
}
}
And don't use == twice in a row, you can't, do:
a == b && b == c
I would write:
bool operator==(const A& lhs, const A& rhs){ /* do actual comparison */ }
And use it twice with and operation.

Quick and dirty operator!=

In my classes I often write a quick operator!= by returning !(*this == rhs), e.g.:
class Foo
{
private:
int n_;
std::string str_;
public:
...
bool operator==(const Foo& rhs) const
{
return n_ == rhs.n_ && str_ == rhs.str_;
}
bool operator!=(const Foo& rhs) const
{
return !(*this == rhs);
}
};
I can't see any obvious problems with doing this but thought I'd ask if anyone knows of any.
I believe that's the preferred method of implementing operator!= so that you don't repeat yourself, and you have a guaranteed correct relationship with operator==.
Defining operator!= as !operator== is just fine
For getting these trivial equivalent operators easily defined, I always use Boost.Operators.
The case with only operator== and operator!= (i.e. using equality_comparable<>) doesn't gain very much.
But when you need less and greater than too, or some combination of operator+, operator* etc. this becomes very convenient.
An example for your case would read
class Foo : private boost::equality_comparable< Foo >
{
private:
int n_;
std::string str_;
public:
...
bool operator==(const Foo& rhs) const
{
return n_ == rhs.n_ && str_ == rhs.str_;
}
};
No, that's absolutely fine - I do exactly the same.