This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++11 status.

### 1262. `std::less<std::shared_ptr<T>>` is underspecified

Section: 20.11.3.8 [util.smartptr.shared.cmp] Status: C++11 Submitter: Jonathan Wakely Opened: 2009-11-10 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [util.smartptr.shared.cmp].

View all issues with C++11 status.

Discussion:

20.11.3.8 [util.smartptr.shared.cmp]/5 says:

> For templates `greater`, `less`, `greater_equal`, and `less_equal`, the partial specializations for `shared_ptr` shall yield a total order, even if the built-in operators `<`, `>`, `<=`, and `>=` do not. Moreover, `less<shared_ptr<T> >::operator()(a, b)` shall return `std::less<T*>::operator()(a.get(), b.get())`.

This is necessary in order to use `shared_ptr` as the key in associate containers because n2637 changed `operator<` on `shared_ptr`s to be defined in terms of `operator<` on the stored pointers (a mistake IMHO but too late now.) By 7.6.9 [expr.rel]/2 the result of comparing builtin pointers is unspecified except in special cases which generally do not apply to `shared_ptr`.

Earlier versions of the WP (n2798, n2857) had the following note on that paragraph:

> [Editor's note: It's not clear to me whether the first sentence is a requirement or a note. The second sentence seems to be a requirement, but it doesn't really belong here, under `operator<`.]

I agree completely - if partial specializations are needed they should be properly specified.

20.11.3.8 [util.smartptr.shared.cmp]/6 has a note saying the comparison operator allows `shared_ptr` objects to be used as keys in associative containers, which is misleading because something else like a `std::less` partial specialization is needed. If it is not correct that note should be removed.

20.11.3.8 [util.smartptr.shared.cmp]/3 refers to '`x`' and '`y`' but the prototype has parameters '`a`' and '`b`' - that needs to be fixed even if the rest of the issue is NAD.

I see two ways to fix this, I prefer the first because it removes the need for any partial specializations and also fixes `operator>` and other comparisons when defined in terms of `operator<`.

1. Replace 20.11.3.8 [util.smartptr.shared.cmp]/3 with the following and remove p5:

   >     template<class T, class U> bool operator<(const shared_ptr<T>& a, const shared_ptr<U>& b);

   > > 3 Returns: `x.get() < y.get()`. `std::less<V>()(a.get(), b.get())`, where `V` is the composite pointer type (7.6.9 [expr.rel]).

   > > 4 Throws: nothing.

   > > 5 For templates `greater`, `less`, `greater_equal`, and `less_equal`, the partial specializations for `shared_ptr` shall yield a total order, even if the built-in operators `<`, `>`, `<=`, and `>=` do not. Moreover, `less<shared_ptr<T> >::operator()(a, b)` shall return `std::less<T*>::operator()(a.get(), b.get())`.

   > > 6 [Note: Defining a comparison operator allows `shared_ptr` objects to be used as keys in associative containers. — end note]

2. Add to 20.11.3 [util.smartptr.shared]/1 (after the `shared_ptr` comparisons)

   >     template<class T> struct greater<shared_ptr<T>>;
   >     template<class T> struct less<shared_ptr<T>>;
   >     template<class T> struct greater_equal<shared_ptr<T>>;
   >     template<class T> struct less_equal<shared_ptr<T>>;

   Remove 20.11.3.8 [util.smartptr.shared.cmp]/5 and /6 and replace with:

   >     template<class T, class U> bool operator<(const shared_ptr<T>& a, const shared_ptr<U>& b);

   > > 3 Returns: `xa.get() < yb.get()`.

   > > 4 Throws: nothing.

   > > 5 For templates `greater`, `less`, `greater_equal`, and `less_equal`, the partial specializations for `shared_ptr` shall yield a total order, even if the built-in operators `<`, `>`, `<=`, and `>=` do not. Moreover, `less<shared_ptr<T> >::operator()(a, b)` shall return `std::less<T*>::operator()(a.get(), b.get())`.

   > > 6 [Note: Defining a comparison operator allows `shared_ptr` objects to be used as keys in associative containers. — end note]

   >     template<class T> struct greater<shared_ptr<T>> :
   >     binary_function<shared_ptr<T>, shared_ptr<T>, bool> {
   >       bool operator()(const shared_ptr<T>& a, const shared_ptr<T>& b) const;
   >     };

   > > `operator()` returns `greater<T*>()(a.get(), b.get())`.

   >     template<class T> struct less<shared_ptr<T>> :
   >     binary_function<shared_ptr<T>, shared_ptr<T>, bool> {
   >       bool operator()(const shared_ptr<T>& a, const shared_ptr<T>& b) const;
   >     };

   > > `operator()` returns `less<T*>()(a.get(), b.get())`.

   >     template<class T> struct greater_equal<shared_ptr<T>> :
   >     binary_function<shared_ptr<T>, shared_ptr<T>, bool> {
   >       bool operator()(const shared_ptr<T>& a, const shared_ptr<T>& b) const;
   >     };

   > > `operator()` returns `greater_equal<T*>()(a.get(), b.get())`.

   >     template<class T> struct less_equal<shared_ptr<T>> :
   >     binary_function<shared_ptr<T>, shared_ptr<T>, bool> {
   >       bool operator()(const shared_ptr<T>& a, const shared_ptr<T>& b) const;
   >     };

   > > `operator()` returns `less_equal<T*>()(a.get(), b.get())`.

[ 2009-11-18: Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]

Proposed resolution:

Replace 20.11.3.8 [util.smartptr.shared.cmp]/3 with the following and remove p5:

>     template<class T, class U> bool operator<(const shared_ptr<T>& a, const shared_ptr<U>& b);

> > 3 Returns: `x.get() < y.get()`. `less<V>()(a.get(), b.get())`, where `V` is the composite pointer type (7.6.9 [expr.rel]) of `T*` and `U*`.

> > 4 Throws: nothing.

> > 5 For templates `greater`, `less`, `greater_equal`, and `less_equal`, the partial specializations for `shared_ptr` shall yield a total order, even if the built-in operators `<`, `>`, `<=`, and `>=` do not. Moreover, `less<shared_ptr<T> >::operator()(a, b)` shall return `std::less<T*>::operator()(a.get(), b.get())`.

> > 6 [Note: Defining a comparison operator allows `shared_ptr` objects to be used as keys in associative containers. — end note]