2

While trying out the new Tie-Interceptor three-way comparison operator <=> I was wondering what would be an example such that

struct Foo {
    /*
       ....
    */
    auto operator<=>(const Foo &rhs) const = default;
};

would lead to a compiler error with

Foo Bar1;
Foo Bar2;
std::strong_ordering(Bar1 <=> Bar2);

but not with

Foo Bar1;
Foo Bar2;
std::weak_ordering(Bar1 <=> Bar2);

What would be an example for Foo? In other words how would Foo not imply substitutability? I know that I could write my own implementation of the operator which returns std::weak_ordering ... less/greater/equivalent but how to force the compiler to do so?

I've read Practical meaning of strong_ordering and weak_ordering among others so far.

bolov
  • 72,283
  • 15
  • 145
  • 224
fiscblog
  • 694
  • 1
  • 12
  • 27
  • 1
    please always use the language tag – bolov Apr 24 '20 at 14:58
  • Do note: The three comparison category types ([cmp.categories]) (the types std​::​strong_­ordering, std​::​weak_­ordering, and std​::​partial_­ordering) are not predefined; if the header ([compare.syn]) is not imported or included prior to a use of such a class type – even an implicit use in which the type is not named (e.g., via the auto specifier in a defaulted three-way comparison or use of the built-in operator) – the program is ill-formed. – NathanOliver Apr 24 '20 at 15:10

2 Answers2

4

... but how to force the compiler to do so?

When you use auto as the return type of defaulted operator<=>, the compiler will pick the common comparison category of all the members. So if you have something like:

// any type that is weakly ordered
struct Weak {
    bool operator==(Weak const&) const;
    std::weak_ordering operator<=>(Weak const&) const;
};

struct Foo {
    Weak w;
    int i;
    auto operator<=>(Foo const&) const = default;
};

Then using <=> on two instances of type Foo will give you a weak_ordering, since that's the common comparison category of Weak and int.

In the same way that given:

struct Bar {
    float f;
    auto operator<=>(Bar const&) const = default;
};

Bar::operator<=> gives you a std::partial_ordering.

There are no core language types that give you a std::weak_ordering, but there are some library types that might:

// some typical C++17 comparable type
struct Widget {
    bool operator==(Widget const&) const;
    bool operator<(Widget const&) const;
};

struct LotsOfWidgets {
    std::vector<Widget> widgets;
    auto operator<=>(LotsOfWidgets const&) const = default;
};

The <=> here returns std::weak_ordering (to avoid having to assume what it is you meant by < and ==).


Or you could simply provide that yourself. You don't have to use auto:

struct WeakInt {
    int i;
    friend std::weak_ordering operator<=>(WeakInt, WeakInt) = default;
};
Barry
  • 286,269
  • 29
  • 621
  • 977
2

weak_ordering is a user's choice, an explicit expression of the meaning of comparison for the type. No fundamental types are weakly ordered (floats use partial_ordering), and most standard library types that are ordered in some way either mirror the ordering of some template parameter or pick a more specific ordering.

So weak_ordering happens by = default only if a subobject of Foo itself uses weak_ordering.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982