Chameleon
Chameleon

Reputation: 2137

Three way comparison replaces all others comparison operators except ==?

In g++ 10, I am trying to use three way comparison, just for experiment.

I read that other operators do not needed anymore (except ==).

But even if I can use operator (it is implemented on the compiler), it does not supersede (or imply) !=.

So, the following code does not work.

#include<iostream>

using namespace std;

struct A
{
    struct Iterator
    {
        size_t index;
        size_t operator*() { return index + 1000; }
        //bool operator!=(const Iterator &a) const { return index != a.index; }
        auto operator<=>(const Iterator &a) const { return index <=> a.index; }
        Iterator &operator++() { ++index; return *this; }
    };
    Iterator begin() { return Iterator{0}; }
    Iterator end() { return Iterator{5}; }
};


int main()
{
    A a;
    auto result = a.begin() <=> a.end();
    for (auto b : a)
        cout << b << "\n";
    cout << (a.begin() != a.end()) << "\n";

    return 0;
}

What am I missing here?

Upvotes: 4

Views: 1718

Answers (3)

Noam Rodrik
Noam Rodrik

Reputation: 550

From cppreference:

In brief, a class that defines operator<=> automatically gets compiler-generated operators <, <=, >, and >=. A class can define operator<=> as defaulted, in which case the compiler will also generate the code for that operator."

The code for != and == is not generated, thus you do need to implement the comparisons.

Upvotes: 2

Barry
Barry

Reputation: 303057

I read that other operators do not needed anymore (except ==).

Right, except == is the key bit. There are two categories of comparison operators:

  • equality operators (==, !=)
  • ordering operators (<=>, <, >, <=, >=)

In each of those categories, the first one I listed (== and <=>) is the primary comparison operator. It is the only operator that you need to define if you want to opt in to that category. If you want equality, provide ==. If you want ordering, provide <=> (and also ==). The other comparison operators are secondary comparison operators - expressions using secondary comparison are, in C++20, rewritten to use the primary comparison operator.

These categories are completely distinct - there is no crossover. A x != y expression can invoke operator==(x, y) or even operator==(y, x) but it will never invoke an operator<=> of any kind.

You have code that requires an equality comparison but don't have the equality operator defined, hence it's ill-formed. To get this to work, you need to add:

bool operator==(const Iterator &a) const { return index == a.index; }
auto operator<=>(const Iterator &a) const { return index <=> a.index; }

Note ==, not !=. You should not declare secondary comparison operators in C++20, unless you have a very specific need for them (and this is not such a need).

For more, see Comparisons in C++20.


The single exception to this rule is, for convenience, if you default operator<=> then you also get a declared, defaulted operator==. It's as if you had defaulted both yourself. In this example, since your comparisons are just the default member-wise comparisons, you could have written:

auto operator<=>(const Iterator &a) const = default;

As your single comparison operator declaration, which would've behaved as if you'd written:

bool operator==(const Iterator &a) const = default;
auto operator<=>(const Iterator &a) const = default;

Which gives you the correct equality operator you need for your program.

Upvotes: 9

Oliv
Oliv

Reputation: 18051

operator == is implicitly declared as defaulted for each operator operator <=> defined as defaulted:

#include<iostream>

using namespace std;

struct A
{
    struct Iterator
    {
        size_t index;
        size_t operator*() { return index + 1000; }
        //bool operator!=(const Iterator &a) const { return index != a.index; }

        auto operator<=>(const Iterator &) const = default;
        // bool operator == (const Iterator&) const; implicitly declared

        Iterator &operator++() { ++index; return *this; }
    };
    Iterator begin() { return Iterator{0}; }
    Iterator end() { return Iterator{5}; }
};


int main()
{
    A a;
    auto result = a.begin() <=> a.end();
    for (auto b : a)
        cout << b << "\n";
    cout << (a.begin() != a.end()) << "\n";

    return 0;
}

Upvotes: 1

Related Questions