C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Let spaceship return an int

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Sun, 24 Sep 2023 17:22:23 -0400
On Sun, Sep 24, 2023 at 4:57 PM Chris Gary via Std-Proposals
<std-proposals_at_[hidden]> wrote:
>
>
>> What does that mean for NaN <=> 1.0 ?
>
>
> NaN <op> NaN is false for <op> : (arg,arg) -> bool
> NaN <=> NaN -> -2
> ..which could be reserved for a ternary, meaning "undefined". Or, any value not, -1, 0, 1.

That's not a reasonable answer. People almost never check such integer
values for exact equivalence with -1 or 1. It's always a comparison
with zero. Indeed, famous 3-way comparison functions like `strlen`
*explicitly* do not return exactly -1 or 1; they only return values
which compare less than, equal to, or greater than 0.

So `NaN <=> NaN` returning -2 should also mean that `NaN < NaN ==
true`. Which it doesn't.

> I proposed int as to avoid exactly what happened in this thread anyway.
> So, in better disclosure, I've had success in the past with a "rich" enum ternary type made for an override of a "Compare" function.
>
> Here's a snippet:
>
> `
> enum class Relation : int
> {
> Undefined = -2,
> Less = -1,
> Equal = 0,
> Greater = 1
> };
>
> inline constexpr Relation ClampToValid( Relation r ) noexcept
> {
> const auto u = (int)r + 1;
> const auto v = 2 - u;
> const auto w = u | ((u|v) >> (bitCount<int>-1));
> return (Relation)( w - 1 );
> }
>
> // Relational operators that take "Relation" etc...
> `
> Now, if <=> could just return `int` or anything that can round-trip to an `int` implicitly, and enum classes could expose implicit conversions, this could all be safely encapsulated without creating incorrect code that tries to compare a `Relation` value and not getting the magic `undefined` result - everything would get mapped into to the set {-2,-1,0,1} before comparison. The "sign of difference" rule still applies everywhere, and comparisons are allowed to return any other random value to mean "undefined". In cases where the meaning of the return value means more than just "undefined", and the code still assumes the semantics of "Relation", it wasn't written to deal with the failure anyway (generally, just capture the original int and propagate an error/throw an exception).

Oh, so what you're saying is that basically every single argument you
made in this thread thus far was a subterfuge. All that talk about
`int` being "easier to use," or "It must be possible to effectively
use the language without the library" and so forth was just a
smokescreen.

Being able to make the spaceship operator do the above is what you
really wanted.

To which I say that the above code is a very good example of why we
*should not* use integers. Why? Because of what I said at first.

`strong_ordering` and the rest of those types are explicitly *not*
convertible from integers. Given an object of an ordering type, the
only things you can test it against are other ordering values or a
zero-equivalent value. Not only does this make 3-way comparison
consistent (you can only compare the result against zero), it prevents
exactly the NaN issue above. If a comparison returns
`partial_ordering::unordered`, comparing it against 0 will always be
false.

Received on 2023-09-24 21:22:36