Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Clarifying std::weak_ordering with C++20 Spaceship Operator for a Struct

I am learning about std::strong_ordering and std::weak_ordering in C++20. According to the docs, std::strong_ordering requires that equivalent values be indistinguishable, as opposed to std::weak_ordering.

When researching std::weak_ordering (especially in the context of the spaceship operator, <=>), I generally encounter examples similar to the one below, which represents a multiplication expression:

// Represents a multiplication expression, the result of which is (multiplicand * multiplier)
struct Multiplication {
    int multiplicand;
    int multiplier;
};

This struct appears like it should be weakly ordered as different pairs of multiplicands and multipliers can evaluate to the same result, meaning equivalent values can be distinguishable (Multiplication{3, 2}, Multiplication{6, 1}, and Multiplication{1, 6} all technically represent 6).

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

However, the trivial approach for comparing different Multiplication structs actually returns an std::strong_ordering because multiplication results are ints and hence strongly ordered.

struct Multiplication {
    int multiplicand;
    int multiplier;

    // This actually returns std::strong_ordering by default
    auto operator<=>(MultiplicationExpression rhs) const 
    {
        return (multiplicand * multiplicator) <=> (rhs.multiplicand * rhs.multiplicator);
    }
};

Is it then my responsibility to explicitly annotate the return type as std::weak_ordering in this case to make it semantically accurate?

>Solution :

According to the docs, std::strong_ordering requires that equivalent values be indistinguishable, as opposed to std::weak_ordering.

This is incorrect. It requires "substitutability", which in the standard is defined as:

the property that f(a) == f(b) is true whenever a == b is true, where f denotes a function that reads only comparison-salient state that is accessible via the argument’s public const members.

That’s a very narrow definition, one that allows for a and b to have differences in data so long as they are not "comparison-salient".

That being said:

Is it then my responsibility to explicitly annotate the return type as std::weak_ordering in this case to make it semantically accurate?

Yes. The compiler cannot know what is "substitutable" and what is not. This is a semantic property, and it is always up to the programmer to define it.

And yes, your Multiplication struct is not substitutable.

Note that it’s easy to fix this:

std::weak_ordering operator<=>(Multiplication const& rhs) const = default;

You don’t even have to define the function.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading