Why can I invoke == with a defaulted <=> but not a user-provided one? [duplicate]
Asked Answered
S

1

7
#include <compare>

struct A
{
    int n;
    auto operator <=>(const A&) const noexcept = default;
};

struct B
{
    int n;
    auto operator <=>(const B& rhs) const noexcept
    {
        return n <=> rhs.n;
    }
};

int main()
{
    A{} == A{}; // ok
    B{} == B{}; // error: invalid operands to binary expression
}

compiled with clang-10 as clang -std=c++20 -stdlib=libc++ main.cpp

Why does A{} == A{} work but not B{} == B{}?

Sarena answered 5/4, 2020 at 8:26 Comment(0)
S
9

In the original design of the spaceship operator, == is allowed to call <=>, but this is later disallowed due to efficiency concerns (<=> is generally an inefficient way to implement ==). operator<=>() = default is still defined to implicitly define operator==, which correctly calls == instead of <=> on members, for convenience. So what you want is this:

struct A {
    int n;
    auto operator<=>(const A& rhs) const noexcept = default;
};

// ^^^ basically expands to vvv

struct B {
    int n;
    bool operator==(const B& rhs) const noexcept
    {
        return n == rhs.n;
    }
    auto operator<=>(const B& rhs) const noexcept
    {
        return n <=> rhs.n;
    }
};

Note that you can independently default operator== while still providing a user-defined operator<=>:

struct B {
    int n;
    // note: return type for defaulted equality comparison operator
    //       must be 'bool', not 'auto'
    bool operator==(const B& rhs) const noexcept = default;
    auto operator<=>(const B& rhs) const noexcept
    {
        return n <=> rhs.n;
    }
};
Squamation answered 5/4, 2020 at 8:30 Comment(3)
@Sarena [over.match.oper]/3.4 (note that the rewritten candidate set of == doesn't include <=>)Squamation
operator<=>() = default is still defined to implicitly define operator==, could you refer me to the page of the standard on this statement?Sarena
@Sarena class.compare.default/5Unkennel

© 2022 - 2024 — McMap. All rights reserved.