Boost logo

Boost :

Subject: Re: [boost] [optional] operator<(optional<T>, T) -- is it wrong?
From: Gavin Lambert (gavinl_at_[hidden])
Date: 2014-11-28 00:15:40


On 28/11/2014 14:08, Vicente J. Botet Escriba wrote:
> Le 27/11/14 23:44, Gavin Lambert a écrit :
>> I'm not sure I understand the meaning of having an order that isn't
>> implied by op<. If op< is omitted because comparison is meaningless
>> or otherwise confusing, how could it be well-defined how to sort
>> them? If there is not a single well-defined sort order, then the
>> library should not just pick one arbitrarily, and if there is, then
>> why would it not be appropriate as an op<?
>
> There are a lot of kinds of orders
> (http://en.wikipedia.org/wiki/Total_order). Some of us would like that T
> < T mean the same kind of order thing independently of T. As we have
> already int < int that represents the natural order it follows that T <
> T should mean then the natural order. We can then have a lot of other
> orders that don't use this specific syntax. The order needed by the
> ordered containers is not the natural order but a strict week order (
> http://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings ).

That doesn't address what I was referring to. My point was that if
there is disagreement on where "none" should sort, or how "complex"
should sort, then the library should not define these as default sorts
(via op< or std::less). Instead it should either provide standard
sorting predicates for the common options or just leave it up to the
application to decide how it wants to sort things.

>>> So it would be nice if std::complex specialized std::order, but didn't
>>> have op<. If we don't get std::order, complex will probably
>>> specialize std::less in the near future, because it is what works
>>> today.
>>
>> I don't agree with any of that. If you want to use it as a key for
>> something map/set-like, use unordered_map/set. I can think of
>> multiple possible orderings of std::complex depending on application
>> need, and I don't think any one of them is inherently "better" than
>> any other. So this should be left to the application to define the
>> sort order, in the rare case that this would actually be needed.
>
> I think you are agreeing here, isn't it?

I am not agreeing that optional or complex should specialise either of
std::less or std::order. I am ok with both defining *type-specific*
sorting predicates, which the application code can choose one of to link
to the generic predicate if it wishes to, or just use explicitly, or
define its own.

>>> optional<T> is Regular if T if Regular. The more that optional<T>
>>> "just works" like int works, the better. Particularly for generic
>>> code.
>>
>> The first part sounds like a definition I am unfamiliar with. I agree
>> with the second part, but I don't think it's relevant to this
>> discussion. Nobody is talking about removing
>> op<(optional<T>,optional<T>), which is what generic code would use.
>
> optional<int> is the sum of none and T. If T has a natural order, the
> natural order of the sum can be defined. So optional<T> should define
> operator < only if T defines it.

Yes, but that does not imply the reverse. If T does not define op< then
optional<T> must not, but if T does define op< then this merely means
that optional<T> *could*, but not whether it *should*.

Another example of a sum type is a variant (tagged union). I think it's
fair to say that using op< on variants with different internal types
(eg. variant holding an int vs. variant holding a string) is not a
reasonable comparison, despite both internal types having a natural
order. (You can try artificially defining one such as lexicographical
order if converted to string, but now you just have two problems.)

The same should apply to optional<T> -- trying to order a value with the
subtype "none" vs. one with the subtype "T" should not be meaningful.

Which kind of sounds like I'm coming around to the side that says
op<(optional<T>,optional<T>) shouldn't exist either.

>> Any generic code that *could* use both T and optional<T> must by
>> definition be aware of the fact that it's using optional, so will
>> either know that op<(optional<T>,T) is not sensible and avoid using it
>> or will explicitly convert as needed so that it invokes
>> op<(optional<T>,optional<T>) instead.
> The problem is not the operator w, but the implicit conversions.

Of course, in absence of a poisoning method, if op<(optional<T>,
optional<T>) and an implicit constructor both exist, then
op<(optional<T>,T) will automatically exist even if not explicitly
defined. Since the discussion related to explicit poisoning that didn't
seem worth mentioning.

>> However possibly there might need to be a type trait specialization
>> that explicitly denies op<(optional<T>,T) and the reverse, to avoid
>> confusing code that attempts to detect when op< does work between
>> distinct types.
> I don't think this follow "make simple things simple".

I'm coming to agree with that, which suggests op< should simply not
exist at all.

>> But you're leaving out A3: remove 1+2 AND std::less/std::order. This
>> means that optional types can't be used in map keys, only in
>> unordered_map. I actually think that this is the best option, except
>> that it has a greater potential to break existing code. Perhaps a
>> predicate could be defined specific to optional giving the current
>> ordering, so that users who do want that ordering or to continue using
>> a map could just specify that predicate as a minimal fix. But I don't
>> think that the predicate should be spelled "std::less".
>>
> Currently we have std::less, not std::order and the STL ordered
> containers are using as default comparator std::less. So let define
> std::less<optional<T>> using std::less<T>.

I realise that this is the current design. But we were discussing an
ideal "alternate" design.

Defining op< for containers was a necessary evil while std::map was the
only standard associative container, and it happened to require ordered
keys. Now that unsorted associative containers exist
(std::unordered_map and its Boost equivalent for pre-C++11), I don't
think its existence can be justified any more.


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