16

In C++, for the operator greater than or equal to (">="), is it enough to have the operators equal ("=") and greater (">") overloaded to have functionality for the greater than or equal to (">=")? Or do I need to overload the operator (">=") to have functionality for it?

4 Answers 4

17

operator >= is not a combination of operator > and operator =. operator >= is its own operator but you can implement it in terms of operator < Typically you would have something like

inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return  operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}

From sbi's answer on What are the basic rules and idioms for operator overloading?

0
13

is it enough to have the operators equal ("=")

Equal operator in c++ is ==

OR do I need to overload the operator (">=") to have functionality for it?

It depends what you mean by functionality. If you mean that if you define operator== and operator> will compiler generate operator>= automagically for you? No, it would not, you have to implement it using existing operators or not.

2
  • Thanks! I thought the compiler will automatically do that.
    – Vasi Marin
    Commented Sep 6, 2018 at 14:02
  • Now in C++20, will the compiler generate the >= automagically ?
    – frakod
    Commented Apr 17, 2022 at 2:20
13

No, C++ does not write those operators for you.

If you think that sucks, you are right. A bunch of ways to make this suck less have been done. I'll talk about 4 of them.

Wait for

In , if you write operator<=> (the 3-way "spaceship" operator) properly, or =default it, then all of <, <=, >=, >, != and == will be written for you.

struct bob {
  int x,y;
  auto operator<=>( bob const& )const = default;
};

The above bob has every < == etc operator written for it by C++ now.

Just write them

Prior to you have to write all of them if you want all of them. This is tedious and error-prone.

Using std::tie and invoking < and the like on them is slightly less error-prone:

struct bob {
  int x, y;
  friend bool operator<( bob const& lhs, bob const& rhs ) {
    return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
  }
};

or even

struct bob {
  int x, y;
  friend auto as_tie( bob const& b ) { // C++14
    return std::tie(b.x, b.y);
  }
  friend bool operator<( bob const& lhs, bob const& rhs ) {
    return as_tie(lhs) < as_tie(rhs);
  }
};

because tuple does a proper lexographic comparison; writing lexographic comparions without bugs is annoying.

Metaprogram your way around it

When comparing strings you usually use strcmp. This returns a negative number if less, a positive number if greater, and 0 if equal. This pattern can be more efficient than doing < or == repeatedly.

Making a single strcmp like function produce < == and the other comparison operations can be done:

namespace utils {
  template<class D>
  struct use_cmp {
    friend bool operator<( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) < 0;
    }
    friend bool operator>( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) > 0;
    }
    friend bool operator<=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) <= 0;
    }
    friend bool operator>=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) >= 0;
    }
    friend bool operator==( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) == 0;
    }
    friend bool operator!=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) != 0;
    }
  private:
    D const& self() const { return *static_cast<D const*>(this); }
  };
}

Now supose we have a type:

struct bob {
  int x, y;
};

and we want to be able to use comparison operators on it:

struct bob : utils::use_cmp<bob>
{
  int x, y;
  bob( int x_, int y_ ):x(x_), y(y_) {} // constructor
  friend int cmp( bob const& lhs, bob const& rhs ) {
    if (lhs.x < rhs.x) return -1;
    if (lhs.x > rhs.x) return 1;
    if (lhs.y < rhs.y) return -1;
    if (lhs.y > rhs.y) return 1;
    return 0;
  }
};

and using the magic of CRTP bob now has every comparison operator written for it.

Live example.

That annoying friend int cmp (which gets more annoying the more members you have in it) can be handled by yet more boilerplate helper code:

namespace utils {
  template<class...Ts>
  int cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs );
  template<class T, class...LowPriority>
  int cmp( T const& lhs, T const& rhs, LowPriority&&... );

  template<class...Ts, std::size_t...Is>
  int tuple_cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs, std::index_sequence<Is...> ) {
    int result = 0;
    ( (result = cmp( std::get<Is>(lhs), std::get<Is>(rhs) )) && ... );
    return result;
  }

  template<class...Ts>
  int cmp( std::tuple<Ts...> const& lhs, std::tuple<Ts...> const& rhs ) {
     return tuple_cmp( lhs, rhs, std::make_index_sequence<sizeof...(Ts)>{} );
  }
  template<class T, class...LowPriority>
  int cmp( T const& lhs, T const& rhs, LowPriority&&... ) {
    if (lhs < rhs) return -1;
    if (rhs < lhs) return 1;
    return 0;
  }
}

which is yet more arcane code, but you get a simpler bob:

struct bob : utils::use_cmp<bob>
{
  int x, y;
  bob( int x_, int y_ ):x(x_), y(y_) {}

  friend auto as_tie(bob const& b) {
    return std::tie(b.x,b.y);
  }
  friend int cmp( bob const& lhs, bob const& rhs ) {
    return utils::cmp( as_tie(lhs), as_tie(rhs) );
  }
};

Note, however, that all of this is done and better by operator<=> in .

Live example.

Use someone else's solution

This is similar to the approach boost::operators uses to write these operators for you.

0
10

Using an obvious notation, "> || ==" is actually an over-requirement for >=.

Although note that for all the relational operators, you only actually need <, since equivalence is established if a < b and b < a are both false. In fact this is one of the concepts used in ordered C++ standard library containers.

4
  • I think it should be > || ==. Thanks for the response!
    – Vasi Marin
    Commented Sep 6, 2018 at 13:54
  • @VasiMarin: Oops. Let's publish jointly!
    – Bathsheba
    Commented Sep 6, 2018 at 13:54
  • 1
    @user463035818: It means that you can derive ">=" from ">".
    – Bathsheba
    Commented Sep 6, 2018 at 14:09
  • 1
    oh sure, maybe "although" should be "because"? I guess thats what caused my confusion, otherwise it completely makes sense Commented Sep 6, 2018 at 14:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.