Re: [boost] Please don't define a fake"operator<"just for ordered containers

More comments - IMHO it is important to come up with a specific set of rules for operator < () than it is to argue about "fake", "feelings", "natural", "expected", and other such terms. What are the semantics of operator <? I'd state the following: 1. It must provide a total ordering (simply a strict weak ordering is not sufficient - for example a strict weak ordering on a pointer could compare just the second byte of the address). 2. Less than ordering must be average constant complexity with worst case complexity proportional to the area of the objects. 3. When there is a strongly established convention for the ordering then that convention should be used. I don't think we have much disagreement on the above points (perhaps some confusion). Now I'd add the following: 4. In the absence of established convention (such as from the domain of mathematics), lexicographical ordering by comparing the parts should be used. Examples from the standard: std::string (which does not sort by established linguistic rules), std::pair, std::vector, std::set, tr1::tuple. Counter examples (which should be fixed): std::complex (real, imaginary). Mathematicians do not have memory or complexity to deal with (mathematicians don't sort) - so arguing that the lack of a rule in mathematics implies that one shouldn't exist in computer science is vacuous. 5. If an object is Incrementable (and/or Decrementable) then the ordering should be such that give: T b(a); ++a; assert (b < a); Examples: pointers within the same array, random access iterators (not forward iterators because of item 2), int This item is potentially debatable (Alex disagrees in his notes - I'll argue it more with him later today). 6. For objects for which a comparison is based upon properties which are not defined across invocations (such as object identity) operator < should not be defined (and not provided where reasonable) Examples: pointers to different arrays, random access iterators to different containers, type_info, forward iterators. For these types, a standard way to get to a total ordering should be provided. Currently we have std::less<> for pointers, type_info::before(), iterators can be converted to pointers - &*iter and then std::less can be used. My recommendation is to use std::less<> - which could be specialized for each of these types. That is std::less<> should provide a total ordering even when operator < does not (but consistent with operator < when it is defined). std::less must still obey 1 and 2. 4a. For composite types for which some of the parts do not define operator < (per item 6). They should define std::less<> based on the lexicographical ordering of the parts using std::less<>. Examples: none - but it would be trivial to specialize std::less<> for pair, tuple, etc. With the above rules, there are _very_ few cases where std::less<> would not be defined (an example would a hash_map where the implementation would violate item 2). If operator < is defined then all comparison operators (>, <=, ==,
=, !=) should be defined consistently. Likewise for std::greater et al.
From item 6 - I'd remove the operator < from shared_ptr. If we were going to keep operator < for shared ptr, then I would have to argue to change all of the examples from 6 for consistency (or provide a semantic difference between shared_ptr and these cases. Sean

Sean Parent wrote:
From item 6 - I'd remove the operator < from shared_ptr. If we were going to keep operator < for shared ptr, then I would have to argue to change all of the examples from 6 for consistency (or provide a semantic difference between shared_ptr and these cases.
There are two problems with using std::less instead of operator< to supply a set/map order. First, it doesn't propagate to composite types; if you provide a specialization of less<K>, less< pair<K, int> > doesn't work. Second, less<K> is defined as always using K::operator< unless K is a raw pointer; it is basically a different name for operator<, one that can be used as a predicate. This gives rise to the interpretation that wherever less<K> is encountered, the implementation is entitled to use K::operator< directly. This could have been avoided by defining a separate relation for set/map order, either as a function reachable via ADL, or as a function object, then making sure that it is defined for all standard value types. With suitable changes to the standard, less<K> can be made that relation, and if this happens, shared_ptr ought to also be changed to reflect that.

On Tue, Jul 11, 2006 at 10:44:50AM -0700, Sean Parent wrote:
What are the semantics of operator <?
I'd state the following: [...] 4. In the absence of established convention (such as from the domain of mathematics), lexicographical ordering by comparing the parts should be used. Examples from the standard: std::string (which does not sort by established linguistic rules), std::pair, std::vector, std::set, tr1::tuple. Counter examples (which should be fixed): std::complex (real, imaginary).
Do I understand correctly that you ask for namespace std { template<class T> bool operator<(const complex<T>& lhs, const complex<T>& rhs); } in the standard? That makes me certainly cringe! complex<T> models the _arithmetic_ type of complex numbers. If you order an arithmetic type A, then the ordering better respects the arithmetic. That is (among other conditions), if a,b,c in A, a < b, and 0 < c, then a * c < b * c. That implies, though, there cannot be an ordering that respects the complex arithmetic. [Let a = 0, b = 1i, c = 1i and assume you have an ordering with 0 < 1i. Then a < b, 0 = a * c > b * c = -1, i.e., your ordering does not respect the arithmetic. (If 0 > 1i, then consider b = c = -1i.)] Sure, you can "forget" the arithmetic structure of the complex numbers and restrict them to their structure as a two dimensional real vector space; I agree this is often useful and you can order finite dimensional vector spaces. Bu then either implement the ordering in a named function or define an appropriate type that models (2D) real vector spaces. Defining operator< for complex<T> for convenience's sake seems to me an abuse of operator overloading.
Mathematicians do not have memory or complexity to deal with (mathematicians don't sort) - so arguing that the lack of a rule in mathematics implies that one shouldn't exist in computer science is vacuous.
You forgot the theory of Groebner bases. Mathematicians most certainly sort, sometimes even with non-standard orders. :-) Regards Christoph -- FH Worms - University of Applied Sciences Fachbereich Informatik / Telekommunikation Erenburgerstr. 19, 67549 Worms, Germany

On Tue, Jul 11, 2006 at 10:44:50AM -0700, Sean Parent wrote:
Mathematicians do not have memory or complexity to deal with (mathematicians don't sort) - so arguing that the lack of a rule in mathematics implies that one shouldn't exist in computer science is vacuous.
That seems to suggest that computer scientists somehow know better than mathematicians about mathematical concepts, which is rather insulting to mathematicians. There is a very good reason why the "rule" (actually a relation) is absent in mathematics: namely that it cannot be defined in a meaningful, useful way, as others have already demonstrated. If it can't be done meaningfully in mathematics, it can't be done meaningfully in computer science either. Paul

Paul Giaccone wrote:
There is a very good reason why the "rule" (actually a relation) is absent in mathematics: namely that it cannot be defined in a meaningful, useful way, as others have already demonstrated. If it can't be done meaningfully in mathematics, it can't be done meaningfully in computer science either.
That's not true. An ordering cannot be defined for the mathematical construct "complex number" that is meaningful and useful to mathematicians. That doesn't mean that the same applies to computer scientists - it is very well possible to define an operator < for std::complex that is useful (allows using the class itself, as well as tuples containing it, to be easily used in sorted containers) and meaningful (it's lexicographical - it certainly has meaning, even if that meaning doesn't make sense to mathematicians). Sebastian Redl

Sebastian Redl wrote:
Paul Giaccone wrote:
There is a very good reason why the "rule" (actually a relation) is absent in mathematics: namely that it cannot be defined in a meaningful, useful way, as others have already demonstrated. If it can't be done meaningfully in mathematics, it can't be done meaningfully in computer science either.
That's not true. An ordering cannot be defined for the mathematical construct "complex number" that is meaningful and useful to mathematicians. That doesn't mean that the same applies to computer scientists - it is very well possible to define an operator < for std::complex that is useful (allows using the class itself, as well as tuples containing it, to be easily used in sorted containers) and meaningful (it's lexicographical - it certainly has meaning, even if that meaning doesn't make sense to mathematicians).
I don't deny that it certainly is possible to define a _useful_ operator, but not one that is meaningful _in the context of what the class is for_. The "complex" class is for storing and working with complex numbers. A user wanting to sort 2-uples should really be using "pair" instead. However, if you want to put complex numbers in a container that requires its contents to be in some order, then by all means provide a lexicographical ordering for the sake of necessity. The sorting is then a computer-scientific issue rather than a mathematical one, and there is then no problem _provided_ the user is made well aware that processing the contents in the order used by the container does not somehow mean that the complex numbers themselves have been sorted into that order; that is, that the ordering is necessary for computing purposes only but cannot be said to apply to the complex numbers themselves. In other words, there is a distinction to be made here between the properties of what is being contained (the complex numbers, which have no ordering relation) and the container (which requires its elements to be sortable in some way or other). Once the distinction is made between the two, there is no longer any contradiction between the way mathematics and computer science treat complex numbers. Paul
participants (5)
-
Christoph Ludwig
-
Paul Giaccone
-
Peter Dimov
-
Sean Parent
-
Sebastian Redl