views:

173

answers:

1

In C++0x (n3126), smart pointers can be compared, both relationally and for equality. However, the way this is done seems inconsistent to me.

For example, shared_ptr defines operator< be equivalent to:

template <typename T, typename U>
bool operator<(const shared_ptr<T>& a, const shared_ptr<T>& b)
{
    return std::less<void*>()(a.get(), b.get());
}

Using std::less provides total ordering with respect to pointer values, unlike a vanilla relational pointer comparison, which is unspecified.

However, unique_ptr defines the same operator as:

template <typename T1, typename D1, typename T2, typename D2>
bool operator<(const unique_ptr<T1, D1>& a, const unique_ptr<T2, D2>& b)
{
    return a.get() < b.get();
}

It also defined the other relational operators in similar fashion.


Why the change in method and "completeness"? That is, why does shared_ptr use std::less while unique_ptr uses the built-in operator<? And why doesn't shared_ptr also provide the other relational operators, like unique_ptr?

I can understand the rationale behind either choice:

  • with respect to method: it represents a pointer so just use the built-in pointer operators, versus it needs to be usable within an associative container so provide total ordering (like a vanilla pointer would get with the default std::less predicate template argument)
  • with respect to completeness: it represents a pointer so provide all the same comparisons as a pointer, versus it is a class type and only needs to be less-than comparable to be used in an associative container, so only provide that requirement

But I don't see why the choice changes depending on the smart pointer type. What am I missing?


Bonus/related: std::shared_ptr seems to have followed from boost::shared_ptr, and the latter omits the other relational operators "by design" (and so std::shared_ptr does too). Why is this?

+9  A: 

A defect has been submitted to change the std::unique_ptr relational operator overloads to use std::less: see LWG Defect 1297.

The defect was submitted in December and still has a state of "New" though, so it's anyone's guess as to whether it will be resolved before C++0x is finished. There are a lot of new and open library defects right now.

James McNellis
Ah, I should of looked there first. :) That definitely answers the change in method, but do you know why `shared_ptr` wouldn't provide all the other relational operators?
GMan
@GMan: I think that might be a mistake. They are listed in 20.9 in the `<memory>` synopsis, but they are not actually present in 20.9.11.2.7...
James McNellis
@James: Oh, good call. I was just scoped on the `shared_ptr` section. Well, that solves that I guess. (They should really clean that up.) Thanks.
GMan
@GMan: Though, the defect may in fact be with the `<memory>` synopsis. Boost and TR1 only specify `==`, `!=`, and `<`. Boost says "The rest of the comparison operators are omitted by design." I haven't found any justification for that design choice on the Boost mailing list or in any of the WG21 papers yet.
James McNellis
More curiously: even though the other three relational operators are not overloaded for `shared_ptr`, the `greater`, `greater_equal`, and `less_equal` class templates are all specialized for `shared_ptr`.
James McNellis
@James: That's awkward. :S I'm searching through proposals trying to see why that's the case. By the way, I'm unaccepting for now so more people will check out the question and these comments. You'll get em back. :)
GMan
@GMan: The Boost rationale seems to be something along the lines of: Because you can't compare arbitrary pointers in C++ using the four relational operators unless those pointers point at subobjects of some array or class and because you'll never have shared_ptrs pointing to subobjects (at least not in the intended use of shared_ptr), they don't implement the relational operators for shared_ptr. However, they want you to be able to use shared_ptr as a map key and for some reason they found it easier to overload op< than to specialize std::less (for compatibility reasons, probably).
James McNellis
That said, I still don't know why shared_ptr and unique_ptr differ with respect to which operators they overload. Regardless, either 20.9 or 20.9.11.2.7 is wrong (they can't both be right). Do you want to submit a defect or shall I?
James McNellis
You shall. :) I don't know how. :S
GMan
@GMan: Nevermind! Take a look at N3126 20.3.1. There are general purpose operator templates in namespace `std` for `!=`, `>`, `<=`, and `>=`. These were even present in C++03 20.2.1. (Well, I've certainly learned something interesting today... I had no idea that those were there!)
James McNellis
@James: Whoa, never saw that. Everything makes much more sense now!
GMan