On Sun, Nov 30, 2014 at 11:12 PM, Gavin Lambert
I'm not sure I want to live in a world where std::less wasn't equivalent to operator<, where the latter exists. (At least at the library layer; if the application wants to mess with it that's their own business.)
I actually tend to agree. I know that as things are now, std::less and operator< have different meaning and I think we need to play by those rules, but that to me has always been a bit wonky, too. If std::less were simply called std::order, that would make things much better. To be honest, though, even separate from that, I'm also in the camp with whoever it was that said that we should simply accept that operator< isn't necessarily "less" but rather is a generalization of less that is considered to mean "order," and which just so happens to be less when in the domain of the built-in arithmetic types. Even then there are still problems, though, because floating point types have things like NaNs that behave in a way that means that neither operator< nor std::less provide order, which is a much bigger horror than < not strictly meaning "less," IMO. At least for pointers we have std::less appropriately specialized. As things are now, you can't even use std::sort on a range of floats or doubles with the default order if there is a chance that you have NaNs, otherwise you have undefined behavior (and it's not even benign undefined behavior in common implementations). With respect to the standard, we have to always be careful to make sure operator< is defined in terms of operator< (similar for other operators) and std::less and family are defined in terms of themselves. Requirement specifications need to accurately reflect this, too. It's unfortunate that even the standard library messes this stuff up when it can on both accounts. It's all unnecessarily confusing and error prone. Tony made very positive progress with respect to optional ordering, but ultimately I think the standard should change at a more basic level as well. There are very few people out there who actually care or know enough to properly define these operations with respect to generic composite types, and the standard is partly to blame for that.
The case in question is where you were using it without realising it; in the stated example perhaps when code that used to have int parameters was changed to optional<int> without amending the operator use accordingly.
That's a programmer error, of course, but it would have been nice if it were also a compiler error. (Sometimes it's less obvious, when there are layers of typedefs and metafunctions in the way, especially when these come from external code.)
I think the specific problem in the original example could actually be remedied with a compiler error, though, while still providing operator< for optional<T>, optional<T> if it is still desired. In the example, the person compared a T with an optional<T>. It was suggested by someone that even if operator overloads for optional<T>, T were removed, that the code would still compile because of the implicit conversion, which isn't actually true. The code would not compile when the operator is specified to be a template as opposed to an inline friend. It seems to me that the least controversial and least-likely way to break valid code and yet also make sure that the problem-case produces an error, if that is desirable, is to just remove any optional<T>, T overloads and make the optional<T>, optional<T> overloads templates. -- -Matt Calabrese