Search code examples
c++comparisonstandardsc++20spaceship-operator

Why can I invoke == with a defaulted <=> but not a user-provided one?


#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{}?


Solution

  • 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;
        }
    };