Why will std::rel_ops::operators be deprecated in C++20?

In C++20, you get three-way comparison (operator <=>), which automatically "generates" default comparisons if provided:

struct A {
   // You only need to implement a single operator.
   std::strong_ordering operator<=>(const A&) const;
};

// Compiler generates 4 relational operators (you need to default the
// three-way comparison operator to get == and !=).
A to1, to2;
if (to1 > to2) { /* ... */ } // ok
if (to1 <= to2) { /* ... */ } // ok, single call to <=>

There are multiple advantages of the three-way comparison over std::rel_ops, which is probably why std::rel_ops operators are deprecated. On top of my head:

  • It is more versatile, since, depending on the return type of operator<=> (std::strong_ordering, std::weak_ordering, ...), only relevant operators are generated. See the <compare> header for more information.

  • You do not bring a bunch of templated operator overloads by doing using namespace std::rel_ops.

  • You can ask the compiler to generate the three-way operator for you by defaulting it (auto operator<=>(A const&) = default) — This will basically generate a lexicographic comparison of base classes and non-static data members, plus it will deduce the right type of ordering if the return type is auto.


What's the rationale behind?

rel_ops was deprecated by Library Support for the Spaceship (Comparison) Operator. The paper doesn't list any motivation, but it does appear in the spaceship paper:

This subsumes namespace std::rel_ops, so we propose also removing (or deprecating) std::rel_ops.

There are four reasons mentioned in the paper (including correctness and performance). But one big one not mentioned in either paper is that std::rel_ops just... doesn't work. Rule of thumb is that operators are found using ADL. rel_ops doesn't give you ADL-findable operators, it just declares unconstrained function templates like:

namespace std {
    namespace rel_ops {
        template< class T >
        bool operator!=( const T& lhs, const T& rhs ) 
        {
            return !(lhs == rhs);
        }
    }    
}

So using algorithms like:

struct X { ... };
bool operator<(X const&, X const&) { ... };
std::sort(values.begin(), values.end(), std::greater<>{});

Just doesn't work, unless you make sure to:

#include <utility>
using namespace std::rel_ops;

Fairly consistently everywhere as your first include to ensure that these operators are visible at the point of definition of every function template you could possibly call.

So operator<=> is just strictly superior:

  • It actually works.
  • You only have to write one function (<=>) instead of two (== and <)
  • Typically, you actually have to write zero functions (= default)
  • Did I mention it actually works?

C++20 provides Three way comparison thus the unique ones will become deprecated.