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
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.