Eyzuky
Eyzuky

Reputation: 1935

Could I use operator == if I only implemented operator <?

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

Answers (13)

Marek R
Marek R

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

Andrew Kashpur
Andrew Kashpur

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

Prithwish Jana
Prithwish Jana

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

Shadi
Shadi

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

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Live example.

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

Foo Is Bar
Foo Is Bar

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

Jonas
Jonas

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

Arne Vogel
Arne Vogel

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

phuclv
phuclv

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

Shitao Zhou
Shitao Zhou

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

xiaobing
xiaobing

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

user7881131
user7881131

Reputation:

C++ cannot infer this automatically for a couple of reasons:

  1. It doesn't make sense for every single type to be compared with operator<, so the type may not necessarily define a operator<.
    • This means that operator== cannot be automatically defined in terms of operator<
  2. operator< isn't required to compare its arguments. A programmer can define operators for their types to do almost anything to their arguments.
    • This means that your statement about !(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

nefas
nefas

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

Related Questions