2

If I am implementing operator < in a C++ class, should I also implement other comparison operators? (operator > and operator == and others)

Or does the class somehow automagically get one from the others?

I want to make my class comparable using STL std::sort and std::priority_queue.

(note: my class has a double member which I am using as an ordering key, so it shares the ordering characteristics of real numbers, which if I understand correctly, makes it a strict total ordering.)

Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 4
    If you want _automagic_, look into C++20's [`operator<=>`](https://stackoverflow.com/questions/47466358/what-is-the-spaceship-three-way-comparison-operator-in-c). – Drew Dormann Mar 24 '23 at 18:49
  • 1
    What you **should** do depends on your use case. Look up the concepts of "partial ordering" and "strict weak ordering". – Karl Knechtel Mar 24 '23 at 18:51
  • What does your C++ textbook say, on this subject? – Sam Varshavchik Mar 24 '23 at 18:59
  • 1
    wow, the "related questions" are particularly bad today. I see only one that's relevant ("idioms for operator overloading") – Ben Voigt Mar 24 '23 at 19:05
  • You should check out [these](https://www.youtube.com/playlist?list=PLHxtyCq_WDLXryyw91lahwdtpZsmo4BGD) lectures by Stepanov. Lecture 2 covers semi-regular, regular, and totally ordered types. – badola Mar 24 '23 at 19:22
  • 1
    The real answer depends on what comparisons your class is required to support. If being comparable with `std::sort()` [and only the variant that accepts two iterators] and `std::priority_queue` is the only requirement, then `operator<()` is sufficient. If code using your class needs to do other comparisons such as `a == b` or `a <= b`, or use other variants of `std::sort()` then you *may* need to implement other operators. In C++20, there is the spaceship operator, which can reduce need to separately implement a number of operators. – Peter Mar 24 '23 at 21:46

2 Answers2

2

If you just want your class be comparable for using std::sort and std::priority_queue, overloading operator< is just fine. As stated here

comparison function object (i.e. an object that satisfies the requirements of Compare) which returns ​true if the first argument is less than (i.e. is ordered before) the second.

operator< function does satisfy the requierment. Also the elements are compared by operator< if custom comparator is not specified.

template< class RandomIt > void sort( RandomIt first, RandomIt last ); (1)

  1. Elements are compared using operator<.

Same applies to std::priority_queue.

So in your case, you can do just with operator<, if your only requierment is to use std::sort and std::priority_queue.

However, if you are going to reverse the order with std::greater, you will need to implement operator>. As stated here

Function object for performing comparisons. Unless specialized, invokes operator> on type T.

However, if you are going only to use std::greater to sort the class. You can do it by a little trick. If you implement operator< as it was operator>, it will do what is intended.

Karen Baghdasaryan
  • 2,407
  • 6
  • 24
1

Other operators are not automatically generated, although C++20 offers operator<=> which can be used to replace a full set of comparison operators.

However, specifically to make the instances orderable for standard library containers, only operator< is needed. Even std::set does not require or use operator==.

Even types that don't implement operator< can be sorted if a comparison predicate is specified explicitly. But there are not formal interfaces to implement here. standard library template classes and algorithms simply use ad-hoc polymorphism: if the operators actually used by the library code are provided, the type is acceptable.

For C++ versions before C++20, it may still be a good idea to implement other comparisons (at least ==) - since other code (possibly your own) might want to use them.

The reason why there is such a mess (optimistically: so much flexibility) here is because there are conceptually many different meaningful semantics for these operators. The way that comparisons work for real numbers (and subsets like integers; thus, C++ types double, float, int etc.) is called total ordering and is the strictest interpretation. On the other hand:

  • Complex numbers can meaningfully be compared for equality, but it's nonsense to say that one is "less than" another when they aren't equal (although their magnitudes are real-valued and can be compared that way, for example).

  • Sets can meaningfully be "less than" one another (normally this is interpreted as a subset relationship), "less than or equal" etc. - but given two unequal sets, it can be (the common case!) that neither is "less than" the other. This is a partial order.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • see update to question re: total/partial ordering; my class objects are ordered based on a `double` member – Jason S Mar 24 '23 at 19:17
  • Yes, you can implement a total ordering in that case. That doesn't change anything about my answer; implementing the other operators will still be a) trivial; b) not necessary for the described use cases; c) possibly still a good idea. In C++20 just use the new toy and don't sweat it. – Karl Knechtel Mar 24 '23 at 19:19