Reputation: 1935
I have implemented operator<
for a certain object.
Logically, if !(a < b)
and !(b < a)
it means a == b
.
Is this inferred automatically? Can I use ==
if I only implement <
?
Upvotes: 52
Views: 10773
Reputation: 37667
There are templates defined in the std::rel_ops
namespace which are auto-defining missing operators.
It doesn't define an equality operator based on the less operator as you wish.
Still this is quite useful; if you define the less operator and equality operator you will have the other comparison operators for free.
Upvotes: 6
Reputation: 746
Consider the following example:
class point{
unsigned int x;
unsigned int y;
public:
bool operator <(const point& other){
return (x+y) < (other.x+other.y);
}
bool operator == (const point& other){
return (x==other.x) && (y==other.y);
}
}
And then we have:
point a{1, 2};
point b{2, 1};
!(a < b), !(b < a) , but also !(a == b).
Upvotes: 3
Reputation: 317
The answer is clear NO. There is no implicit way. C++ classes allow operators to be overloaded. So, your idea that logically, if !(a < b) and !(b < a)
it means a == b
. is correct. And, you can overload operators as below. For example, a Fraction class:
class Fraction {
int num;
int denom;
. . .
public:
. . .
bool operator < (const Fraction &other) {
if ((this->num * other.denom) < (this->denom * other.num))
return false;
else
return true;
}
bool operator == (const Fraction &other) (
if (!(*this < other) && !(other < *this)) {
return true;
else
return false;
}
};
Upvotes: 1
Reputation: 1771
In addition to other answers,
The compiler cannot even infer !=
from ==
struct MyType
{
int value;
};
bool operator == (const MyType& a, const MyType& b)
{
return a.value == b.value;
}
int main()
{
MyType a = {3};
MyType b = {4};
if (a != b) // (* compilation Error *)
std::cout << "a does not equal b" << std::endl;
}
It would be nice though if there is an option to tell the compiler that the rest of the rational operators apply to your class.
There is, as explained in some answers in the <utility>
header something that can provide such functionality. you will need to add the following line at the beginning of the main
:
using namespace std::rel_ops;
However, using this approach is costly and will cause overload ambiguities all over the place as noted by JDługosz.
Upvotes: 4
Reputation: 275385
As many have stated, no you cannot, and no the compiler should not.
This doesn't mean it shouldn't be easy to go from a <
to ==
and the whole myriad.
boost::operators attempts to make it easy. Use it and done.
If you want to do it yourself, it also only takes a little bit of code to reimplement what boost provides you:
namespace utility {
namespace details {
template<class...>using void_t=void;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = ::utility::details::can_apply<Z,void,Ts...>;
}
namespace auto_operators {
template<class T, class U>
using less_r = decltype( std::declval<T const&>() < std::declval<U const&>() );
template<class T, class U>
using can_less = ::utility::can_apply<less_r, T, U>;
struct order_from_less {
template<class T, class U>
using enabled = std::enable_if_t<
std::is_base_of<order_from_less, T>{}
&& std::is_base_of<order_from_less, U>{}
&& can_less<T, U>{},
bool
>;
template<class T, class U>
friend enabled<U,T>
operator>(T const& lhs, U const& rhs) {
return rhs < lhs;
}
template<class T, class U>
friend enabled<U,T>
operator<=(T const& lhs, U const& rhs) {
return !(lhs > rhs);
}
template<class T, class U>
friend enabled<T,U>
operator>=(T const& lhs, U const& rhs) {
return !(lhs < rhs);
}
};
struct equal_from_less:order_from_less {
template<class T, class U>
using enabled = std::enable_if_t<
std::is_base_of<order_from_less, T>{}
&& std::is_base_of<order_from_less, U>{}
&& can_less<T, U>{} && can_less<U,T>{},
bool
>;
template<class T, class U>
friend enabled<U,T>
operator==(T const& lhs, U const& rhs) {
return !(lhs < rhs) && !(rhs < lhs);
}
template<class T, class U>
friend enabled<U,T>
operator!=(T const& lhs, U const& rhs) {
return !(lhs==rhs);
}
};
}
The above only has to be written once, or equivalent cose gotten from #include
boost.
Once you have boost, or the above, it is as simple as something like:
struct foo : auto_operators::equal_from_less {
int x;
foo( int in ):x(in) {}
friend bool operator<( foo const& lhs, foo const& rhs ) {
return lhs.x < rhs.x;
}
};
and foo
now has all the ordering and comparison operators defined on it.
int main() {
foo one{1}, two{2};
std::cout << (one < two) << "\n";
std::cout << (one > two) << "\n";
std::cout << (one == two) << "\n";
std::cout << (one != two) << "\n";
std::cout << (one <= two) << "\n";
std::cout << (one >= two) << "\n";
std::cout << (one == one) << "\n";
std::cout << (one != one) << "\n";
std::cout << (one <= one) << "\n";
std::cout << (one >= one) << "\n";
}
The point of all of this is that C++ doesn't, as a language, assume that <
means >
and >=
and ==
all make sense. But you can write a library that lets you take a type with <
defined, and adding a trivial base class suddenly make all of those other operations defined with zero runtime cost.
Upvotes: 6
Reputation: 31
Kind of.
But you would need boost::operators
Overloaded operators for class types typically occur in groups. If you can write x + y, you probably also want to be able to write x += y. If you can write x < y, you also want x > y, x >= y, and x <= y. Moreover, unless your class has really surprising behavior, some of these related operators can be defined in terms of others (e.g. x >= y <=> !(x < y)). Replicating this boilerplate for multiple classes is both tedious and error-prone. The boost/operators.hpp templates help by generating operators for you at namespace scope based on other operators you've defined in your class.
Upvotes: 2
Reputation: 7017
C++ does not infer this automatically. For operator>
, operator<=
and operator>=
, you could use std::rel_ops
; this requires only operator<
. However, it does not provide operator==
in terms of operator<
. You can do this yourself like this:
template <class T>
bool operator==(T const& lhs, T const& rhs)
{
return !((lhs < rhs) or (rhs < lhs));
}
Note that: !((lhs < rhs) or (rhs < lhs))
and !(lhs < rhs) and !(rhs < lhs)
are equivalent, mathematically.
Upvotes: 12
Reputation: 6676
Logically, if !(a < b) and !(b < a) it means a == b. Does c++ infer this automatically? Can I use == if I only implemented
To put what others have stated in mathematical terms: Assuming that you have an operator <
that returns bool
and defines a strict weak order, and you implement operator ==
as returning !(a < b) && !(b < a)
, then this operator defines an equivalence relation consistent with the given strict weak order. However, C++ neither requires operator <
to define a strict weak order, nor operator ==
to define an equivalence relation (although many standard algorithms such as sort
may implicitly use these operators and require a strict weak order rsp. equivalence relation).
If you want to define all the other relational operators based on and consistent with your operator <
's strict weak order, Boost.Operators may save you some typing.
Because it's so easy to misuse an operator <
that does not meet the standard algorithm's requirements, e.g. by accidentally using it via std::sort
, std::lower_bound
etc., I recommend to define operator <
either as a strict weak order or not at all. The example CodesInChaos gave is a partial order, which does not meet the "transitivity of incomparability" requirement of a strict weak order. Therefore, I'd recommend calling such a relation by a different name, e.g. bool setLess(const MySet &, const MySet &)
.
Sources:
Upvotes: 12
Reputation: 41774
It cannot infer ==
from <
because not all types are ordered, like std::complex
. Is 2 + 3i > 1 + 4i
or not?
Moreover even in types that are normally ordered you still can't infer the equality from >
or <
, for example IEEE-754 NaN
double n = std::numeric_limits<double>::quiet_NaN();
std::cout << "NaN == NaN: " << (n == n) << '\n';
std::cout << "NaN < NaN: " << (n < n) << '\n';
std::cout << "NaN > NaN: " << (n > n) << '\n';
std::cout << "NaN != NaN: " << (n != n) << '\n';
They'll all return false except the last one
Upvotes: 48
Reputation: 181
No. This method works well on number-like objects that is called totally ordered. For all kinds of set/class, no one can guarantee this relation. Even no one can guarantee a operator <
would compare something.
So ==
is nothing else than ==
. You may implement ==
by <
but this doesn't work for everyone and C++ standards won't do it for you.
Upvotes: 18
Reputation: 378
The answer is NO, you just need a simple test
struct MyType{
int value;
};
bool operator < (MyType& a, MyType& b)
{
return a.value < b.value;
}
int main(int argc, char* argv[])
{
MyType a = {3};
MyType b = {4};
if (a == b)
std::cout << "a==b" << std::endl;
if (a < b)
std::cout << "a < b" << std::endl;
}
g++ 4.8.2 complains:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:16:11: error: no match for ‘operator==’ (operand types are ‘MyType’ and ‘MyType’)
But there is something similar that works in C++, check this c++ concepts:Compare
it says:
equiv(a, b), an expression equivalent to !comp(a, b) && !comp(b, a)
Upvotes: 5
Reputation:
C++ cannot infer this automatically for a couple of reasons:
operator<
, so the type may not necessarily define a operator<
.
operator==
cannot be automatically defined in terms of operator<
operator<
isn't required to compare its arguments. A programmer can define operators for their types to do almost anything to their arguments.
!(a < b) && !(b < a)
being equivalent to a == b
may not necessarily be true, assuming those operators are defined.If you want an operator==
function for your types, just define one yourself. It's not that hard :)
// For comparing, something like this is used
bool operator==(const MyType& lhs, const MyType& rhs)
{
// compare (or do other things!) however you want
}
// ... though it's not the only thing you can do
// - The return type can be customised
// - ... as can both of the arguments
const MyType& operator==(int* lhs, const MyType* const rhs)
{
return lhs;
}
Upvotes: 70
Reputation: 1130
The compiler doesn't infer ==
from <
.
You can check that with a simple example:
#include <iostream>
struct A {
A(int r):i{r}{}
int i;
};
bool operator<(A const & a1, A const& a2) {
return a1.i < a2.i;
}
int main(int argc, char* argv[]) {
A a1{2};
A a2{3};
if(a1 == a2) {
std::cout << "equals\n";
}
return 0;
}
GCC
gives you this error:
main.cpp:20:11: error: no match for 'operator==' (operand types are 'A' and 'A')
if(a1 == a2) {
Upvotes: 6