
On 7/5/06 1:38 PM, "Yuval Ronen" <ronen_yuval@yahoo.com> wrote:
Daryle Walker wrote: [Yuval wrote:]
Because operator== has only one purpose in C++: comparison for equality. Operator<, on the other hand, has two common roles.
For classes that have natural ordering, it should define this ordering, and then all the other operators (> <= >=) should be defined as well. Classes that doesn't have this natural ordering, often still define operator< just to be able to be used in ordered containers. In such cases, operator< doesn't define a natural ordering because there isn't any. Instead, it just defines an ordering "that works" for ordered containers, nothing more. It doesn't need to compatible with operator== and !=, and defining operators >, <=, >= is misleading and should be avoided.
The later role is invalid. The reason is that the version of STL that made it into the Standard specifies an extra parameter for ordered containers for a comparison operation. It allows two containers of the same element type to use different comparison criteria (at compile- and/or run-time). This invalidates the need for a fake "operator <" since you can always package the comparison routine in an extra parameter. Conversely, calling code must not hard code use of "operator <" or "std::less<>", unless that code requires types with a natural ordering. (The standard algorithms have variants that either assume "operator <" or take in a comparison parameter.)
Making a fake "operator <" just for ordered containers invalidates the work of those who added comparison parameters in standard classes/functions.
You have a point there, no doubt. On the other hand, Library writers should supply some kind of functor to be used with their types in conjunction with ordered containers (when appropriate, and boost::variant is of course appropriate). How should this functor be called? You might say there should be some convention, but there isn't any right now, and some libraries use operator< for this purpose. Is it that bad if operator< is the convention? I'm not sure. It's sure is convenient to use ordered containers like that... And it seems to me that there is no major drawback to this, because no other order operator is provided. But I guess it's highly dependent on taste...
C++ classes are supposed to model some user concept. If that concept doesn't support ordering then it should not have the ordering operators. Adding those operators for "compatibility" with standard containers is lying to the user by implying an inappropriate capability. I think the problem stems from another trap: default template parameters are Golden and Must Never Be Overridden. Default parameters can be convenient, but you must not use them if they are inappropriate. If they were supposed to be inviolate, wouldn't they would have been hard-coded? To answer your question, I don't think there should be a convention for a non-ordered type so the author can choose the most descriptive name. This name can't be automated because the current automation convention involves using "operator <" and/or "std::less<>", which we assume aren't defined for our non-ordered type. Due to no automation, the user always has to fill in the author's comparison type. Aside: this controversy assumes that there is only one fake ordering possible. What if there's more than one equally good fake order? Do you bless just one? Or do you offer all of them?
The issue is different for the "==" and "!=" operators because two objects can have their state checked for equivalence without having a standardized order. The "std::complex<>" template classes are an example.
std::complex doesn't have operator<. std::tr1::shared_ptr does. So it seems the standard isn't very helpfully consistent here...
Looking at <http://www.boost.org/libs/smart_ptr/shared_ptr.htm>, i.e. the Boost version, it seems that the authors fell into the "fake operator <" trap. If any of those authors are reading this, can you tell us why you added the fake operator instead of a custom-named comparison class?
boost::shared_ptr is a good example of such a class with no natural ordering, with operator== and !=, with operator<, but without the other operators. And shared_ptr is of TR1 strength...
Ordering for pointers is generally defined if both pointers are part of the same array segment. (Either one or both of the pointers can be at the "one-past-the-end" point of said segment.) If two "shared_ptr" objects can be used for points in an array segment[1], then comparison can be allowed with ALL four operators ("<", ">", "<=", and ">="). Otherwise, none of those operators should be defined and any current existence of such operators should be considered a bug.
I didn't quite understand that last paragraph. shared_ptrs doesn't have to point to objects in array (actually, they better not), but there can be arrays of shared_ptrs. So should the shared_ptr class provide order operators or shouldn't it? Anyway, the committee already made up it mind about the answer to this question...
I meant your first case, the one you stated as "shared_ptrs doesn't have to point to objects in array (actually, they better not)". As long as the shared_ptr uses a do-nothing delete function object, I think using it with objects from an array segment is OK. This case is the only time I would feel OK with an "operator <" for a shared_ptr, because it would be a true ordering operator. We would have to add the other three ordering operators to complete ordering capabilities of the model. As is, though, the current operator is a fake; it doesn't even have to be compatible with "std::less<>" (assuming the TR1 shared_ptr is just like the Boost one). Unfortunately, it seems that the committee made a mistake and is endorsing an bad idiom. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com