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.