Home > Software design >  C spaceship-operator and user-defined types: comparing a subset of attributes only
C spaceship-operator and user-defined types: comparing a subset of attributes only

Time:07-10

I have two classes. The first one composing the second one. Both classes have its own synthetized attribute that doesn't collaborate to either ordering or comparision. In addition, I want to usestd::ranges::sort over a container of the second one, so I need to implement a strong-ordering. That's what I have:

struct basic
{
    std::string synthetized; // string representation of the next field.
    unsigned value;
    unsigned property;

    friend bool operator<(basic const& a, basic const& b)
    { return a.value < b.value or (a.value == b.value and a.property < b.property); }
};

struct composed
{
   basic value;
   custom_type meta;

   friend bool operator<(composed const& a, composed const& b)
   { return a.value < b.value; }
};

int main()
{
   std::vector<composed> container;
   // populating container

   // Compilation error here.
   std::ranges::sort(container);
}

How should I appropiately efficiently overload operator<=>? Although what I need is to sort the vector and that's it (I could just go to traditional std::sort), I want to know how to give to both classes full relational capabilities (<, <=, ==, !=, etc) to both classes, but ignoring synthetized and meta, and without doing anything stupid performance-wise.

CodePudding user response:

How should I appropiately efficiently overload operator<=>?

The simplest way to do both would probably be to use std::tie and use the existing function template for operator<=> for tuple types:

template< class... TTypes, class... UTypes >
constexpr /* see link */ operator<=>( const std::tuple<TTypes...>& lhs,
                                      const std::tuple<UTypes...>& rhs );

Example:

#include <tuple>

struct basic {
    std::string synthetized; // excluded from comparisons
    unsigned value;
    unsigned property;

    friend auto operator<=>(basic const& a, basic const& b) {
        return std::tie(a.value, a.property) <=> std::tie(b.value, b.property);
    }
};

struct composed {
    basic value;
    custom_type meta; // excluded from comparisons

    friend auto operator<=>(composed const& a, composed const& b) {
        return a.value <=> b.value;
    }
};

You should also add operator== which will also be used for != so you don't have to overload operator!=.

CodePudding user response:

I want to know how to give to both classes full relational capabilities (<, <=, ==, !=, etc) to both classes, but ignoring synthetized and meta, and without doing anything stupid performance-wise.

Define comparison member functions for your type

#include <string>

struct basic
{
    std::string synthetized; // string representation of the next field.
    unsigned value;
    unsigned property;

    constexpr auto operator<=>(const basic& other) const noexcept {
      if (auto c = value <=> other.value; c != 0) 
        return c;
      return property <=> other.property;
    }

    constexpr bool operator==(const basic& other) const noexcept {
      return value == other.value && property == other.property;
    }
};

struct composed
{
   basic value;
   custom_type meta;

   constexpr auto operator<=>(const composed& other) const noexcept {
    return value <=> other.value; 
   }

   constexpr bool operator==(const composed& other) const noexcept {
    return value == other.value; 
   }
};

Note that in your example operator<=> does not synthesize operator== because it is not the default, so you still need to define operator== for your type to satisfy total_ordered_with.

Demo

  • Related