Boost logo

Boost :

From: Daryle Walker (darylew_at_[hidden])
Date: 2006-07-04 19:05:05


[I know that this is from a thread that died a month ago, but I want to give
advice to people who just want to add an "operator <" for ordered-container
compatibility.]

On 6/9/06 12:06 PM, "Yuval Ronen" <ronen_yuval_at_[hidden]> wrote:

> Eric Friedman wrote:
>>>> As for operator!= (and operator<=, etc.), while I do think it may be
>>>> useful, it raises the question of what requirements variant imposes on
>>>> its bounded types. Specifically, adding support for these operators is
>>>> not so straightforward as saying operator!= is simply !(*this == rhs).
>>>> The idea of operator== and operator< is that if the current bounded type
>>>> is the same in both variant instances, then operator overload _for that
>>>> type_ will be invoked. To support this same behavior for the other
>>>> operators would require adding to the bounded type requirements. See
>>>> <http://boost.org/doc/html/variant/reference.html#variant.concepts> for
>>>> a refresher on variant's approach to concepts. Maybe this is an
>>>> overly-pedantic response on my part, though.
>>>
>>> I think it *is* as straightforward as saying operator!= is simply
>>> !(*this == rhs). There is no other way that makes sense.
>>
>> I of course agree with you that this definition of operator!= makes
>> sense, but I am reluctant because this is not how C++ works: the
>> language allows entirely separate definitions of operator== and
>> operator!=. It would seem unfortunate I think if variant called
>> operator==(T,T) but never operator!=(T,T)...
>
> It's true that C++ allows for entirely separate definitions of
> operator== and operator!=, but we have to make the assumption that these
> definitions always compare for equality. In fact, you made this
> assumption yourself when implementing variant's operator==. The code
> that returns false if this->which() != rhs.which() is exactly such
> assumption - why return false and not some other thing?
>
> That said, if you choose to implement operator!= using T's operator!=,
> then I'd be completely cool with that.
>
>>> Operators >, <=, >=, on the other hand, are completely different. I
>>> think there is no natural ordering of variant objects, so not supporting
>>> them is logical. Operator < is an exception because it's used for
>>> ordered conrainers. IOW, I completely agree with your decision to supply
>>> operator < and not supplying operators >, <=, >=.
>>
>> Now how is that operator!= as !(lhs == rhs) makes sense, but operator<=
>> as (lhs < rhs || lhs == rhs) is completely different?
>
> 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.

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.

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

> The question we need to ask, is whether variant has a natural ordering,
> and I think the answer is "no". All the rest is just a consequence of it.
[TRUNCATE]

[1] Obviously, such pointers can't use the standard "operator delete" since
they couldn't be created with the standard single-new operator.

-- 
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT hotmail DOT com

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