Boost logo

Boost :

From: Sean Parent (sparent_at_[hidden])
Date: 2006-07-11 13:44:50


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


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