ISO/IEC JTC1 SC22 WG21 N3764 - 2013-09-02
Ville Voutilainen, ville.voutilainen@gmail.comThis paper enumerates existing practice for relational operators, enumerates various proposals for changing/fixing the relational operators for std::optional, and proposes to apply a strategy consistent with the relational operators of std::tuple.
There are various inconsistencies with the existing practice:
There was quite a lot of discussion about what the relational operators of optional should do. The controversy arises between two views: according to one, optional should be consistent with other types in the standard. According to the other, optional should be as transparent as possible, and as similar to its underlying type as possible. The first view is quite straightforward, the operators would be synthesized. The second view would allow optional to work like types that eg. have no operator>, in that case optional wouldn't have that operator either. The second view would also treat optional<double> the same way as raw double as far as relational operators and NaNs are concerned.
Stepanov has some interesting words to say about NaNs in his paper http://www.stepanovpapers.com/notes.pdf (pages 24-25). The author of this paper doesn't think optional should try to cater for NaNs or types that define just a part of relational operators but not all of them. Standard tuple and containers don't, their strategy is to define operator<, and then define all the other relational operators in terms of operator<. The author of this paper finds being consistent a much stronger argument than the perceived transparency of optional.
It seems quite natural for many people to think of optional<T> to be just a T with an additional value (nullopt). Hence it's according to expectations that optional<T> would not have relational operators that the underlying T doesn't have. Tony Van Eerd points out that complex is an interesting example; it doesn't have operator<, but it has a specialization of std::less, so it would be awkward if optional<complex<T>> has an operator< but complex<T> does not.
The author of this paper is mystified by the lack of operator!= for optionals. As Marc Glisse pointed out, it works just fine even for NaN doubles even if operator!= is defined as the boolean opposite of operator==.
It thus far seems that both the synthesis proponents and opponents agree that optional should have std::less specializations, to make optional<T*> and optional<complex<T>> work properly with ordered associative containers. The remaining debate seems to be between synthesizing and not synthesizing. It seems to the author of this paper that std::complex is not an issue; the synthesized operators would all be ill-formed since operator< is, and the rest would be defined in terms of operator<. It also seems that both camps are beginning to agree that optional's relational operators should not use std::less (mostly because of the case with complex) but should invoke the underlying type's relational operators. The synthesis would give an operator> for an optional<T> even if T has just operator<, the non-synthesis wouldn't.
This paper proposes to make optional's relational operators
do the same thing that tuple and containers do, aka synthesize
the operators without using std::less, and in addition to that,
add std::less specializations for containers/tuples/optionals holding
pointers or std::complex. If the National Body of the author has
done its job, there are NB comments on the C++14 CD requesting
the same (although they probably don't take complex into account).
Daniel Krügler has proposed that such specialization
should be written as variadic templates, so that we wouldn't need to specialize
just for pointers, roughly like
less<tuple<T...>>
, which would also work for complex and nested tuples of tuples of vectors
of optionals, when all of these have a proper less specialization.