Boost logo

Boost :

From: Gavin Lambert (boost_at_[hidden])
Date: 2023-02-20 04:14:40


On 20/02/2023 12:08, Peter Dimov wrote:
> Gavin Lambert wrote:
>> (Also, can someone explain why x == y <==> y == x is controversial? I have a
>> hard time picturing a use case where you deliberately intend these to be non-
>> commutative, outside of pathological library-fights.)
>
> Why is this "pathological"? If you allow heterogeneous comparisons, who
> decides the meaning of x == y, the author of x or the author of y?

The correct answer is usually "whichever type was created last" (or more
correctly, whichever one most naturally can depend on the other) and is
thus aware of the other and capable of providing the appropriate
conversion/comparison, either directly in the equality operator or via
conversion operators (though the latter have their own sets of problems).

This has historically been a bit more problematic in C++ libraries with
insufficiently-constrained template code, because until Concepts,
constraining was a lot harder, so many types didn't bother, or used
techniques that are more problematic with the current standard wording
(e.g. return-type SFINAE). Applications are more likely to use concrete
types, so are less vulnerable to this.

Basically, operator==(A,B) and operator==(B,A) should both be defined by
A, neither by B. That means that if one implements the single-parameter
instance method it should also implement the two-parameter reversed
friend (and *not* leave that to the other class's instance).

This is a natural rule anyway, because trying to do otherwise creates
mutual dependency loops. If A knows about B then B should not know about A.

(You could often get away with that before when A and B are from the
same library, but that's still a mutual dependency loop, which often
requires implementation hoops.)

The main things C++20 brings to the table are to allow (but not require)
only one of the operator== to be implemented and not require operator!=
to be implemented, which simplifies the code. Unless these have
pathological implementations, you shouldn't care which one it chooses to
actually call (if you do define more than one), since they should all
produce the same result. If there are significant performance
differences between them, then perhaps you shouldn't implement the one
that has worse performance, or spell it differently from operator== as a
hint to the user that it's a more expensive operation, so they don't use
it accidentally.

The only other reason I can think of to have order-sensitive behaviour
of supposedly commutative operators is if you're abusing the operator
overloading for a compiled DSL... but arguably operator== still ought to
be commutative in a DSL, or you're probably being more abusive than you
should be.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk