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

How to properly implement operator<=> for a non-POD class?


I'm looking for a way to implement the three-way comparison operator and the operator== for the following class:

class Foo
{
public:
    auto operator<=>( const Foo& rhs ) const noexcept = default;

private:
    std::uint32_t m_Y;
    std::uint32_t m_X;
    char m_C;
    std::vector<char> m_Vec;
};

But the default implementation is not what I intended. Therefore I need to write my own implementation.

What I want is:

  1. I want the equality comparison (== and !=) to be based on comparing the members m_Y, m_X, and m_C of two operands (of type Foo). All of those three members must be equal to satisfy the equality. The equality of the content of m_Vec is not important (since it's not efficient to compare all those elements lexicographically).
  2. I want the ordering comparison (< and >) to be based on comparing the expression m_Y * m_X.
  3. I want the ordering comparison (<= and >=) to be based on comparing the expression m_Y * m_X and if both operands are equal in that regard, then all three members of both operands should be equal to satisfy the <= or >= (just like in ==).

Also with all this said, which comparison category type suits this scenario better, std::strong_ordering or std::weak_ordering?

How should I implement such logic? It seems simple and I read a whole recipe in a C++20 book on this topic and I still can't figure it out.


Solution

  • I believe this is a partial order, which is an order where elements may be incomparable (none of <, >, or == hold between them). You should verify the necessary laws hold (a <= b iff a < b || a == b, a <= a for all a, a == b if a <= b && b <= a for all a, b, and a <= c if a <= b && b <= c for all a, b, c). If that is the case, use std::partial_ordering.

    std::partial_ordering operator<=>(Foo const &other) const noexcept {
        // compare your * expression first
        std::partial_ordering ret = (this->m_Y * this->m_X) <=> (other.m_Y * other.m_X);
        if(ret != 0) return ret;
        // if that's not informative, compare the fields
        return std::tie(this->m_Y, this->m_X, this->m_C) == std::tie(other.m_Y, other.m_X, other.m_C)
               ? std::partial_ordering::equivalent : std::partial_ordering::unordered;
    }