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

### 1289. Generic casting requirements for smart pointers

Section: 20.2 [utility] Status: NAD Submitter: Ion Gaztañaga Opened: 2009-12-14 Last modified: 2019-02-26

Priority: Not Prioritized

View all other issues in [utility].

View all issues with NAD status.

Discussion:

In section 16.4.4.6 [allocator.requirements], Table 40 — Allocator requirements, the following expression is required for allocator pointers:

> Table 40 — Allocator requirements
> Expression | Return type | Assertion/note pre-/post-condition | Default
> --- | --- | --- | ---
> `static_cast<X::pointer>(w)` | `X::pointer` | `static_cast<X::pointer>(w) == p` |

To achieve this expression, a smart pointer writer must introduce an explicit conversion operator from `smart_ptr<void>` to `smart_ptr<T>` so that `static_cast<pointer>(void_ptr)` is a valid expression. Unfortunately this explicit conversion weakens the safety of a smart pointer since the following expression (invalid for raw pointers) would become valid:

>     smart_ptr<void> smart_v = ...;
>     smart_ptr<T> smart_t(smart_v);

On the other hand, `shared_ptr` also defines its own casting functions in 20.11.3.10 [util.smartptr.shared.cast], and although it's unlikely that a programmer will use `shared_ptr` as `allocator::pointer`, having two different ways to do the same cast operation does not seem reasonable. A possible solution would be to replace `static_cast<X::pointer>(w)` expression with a user customizable (via ADL) `static_pointer_cast<value_type>(w)`, and establish the `xxx_pointer_cast` functions introduced by `shared_ptr` as the recommended generic casting utilities of the standard.

Unfortunately, we've experienced problems in Boost when trying to establish `xxx_pointer_cast` as customization points for generic libraries (http://objectmix.com/c/40424-adl-lookup-explicit-template-parameters.html) because these casting functions are called with explicit template parameters and the standard says in 13.10.2 [temp.arg.explicit] p.8 "Explicit template argument specification":

> 8 ...But when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call. If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply.

So we can do this:

>     template<class BasePtr>
>     void generic_ptr_swap(BasePtr p)
>     {
>       //ADL customization point
>       swap(p, p);
>       //...
>     }

but not the following:

>     template<class BasePtr>
>     void generic_ptr_algo(BasePtr p)
>     {
>       typedef std::pointer_traits<BasePtr>::template
>          rebind<Derived> DerivedPtr;
>       DerivedPtr dp = static_pointer_cast<Derived>(p);
>     }

The solution to make `static_pointer_cast` a customization point is to add a generic declaration (no definition) of `static_pointer_cast` in a namespace (like `std`) and apply "`using std::static_pointer_cast`" declaration to activate ADL:

>     namespace std{

>     template<typename U, typename T>
>     unspecified
>     static_pointer_cast(T&&) = delete;

>     }

>     template<class BasePtr>
>     void generic_ptr_algo(BasePtr p)
>     {
>       typedef std::pointer_traits<BasePtr>::template
>          rebind<Derived> DerivedPtr;

>       //ADL applies because static_pointer_cast is made
>       //  visible according to [temp.arg.explicit]/8
>       using std::static_pointer_cast;

>       DerivedPtr dp = static_pointer_cast<Derived>(p);

>       //...
>     }

A complete solution will need also the definition of `static_pointer_cast` for raw pointers, and this definition has been present in Boost (http://www.boost.org/boost/ pointer_cast.hpp) for years.

[ 2010-03-26 Daniel made editorial adjustments to the proposed wording. ]

[ Moved to NAD Future at 2010-11 Batavia ]

> This is a new feature rather than a defect. It can be added later: "this is such a hairy area that people will put up with changes"

[LEWG Kona 2017]

Recommend NAD: NAD. Should bring a paper as a proposal for 2020.

Proposed resolution:

Add to section 20.2 [utility] Utility components, Header `<utility>` synopsis:

>     // 20.3.X, generic pointer cast functions

>     template<typename U, typename T>
>     unspecified
>     static_pointer_cast(T&&) = delete;

>     template<typename U, typename T>
>     unspecified
>     dynamic_pointer_cast(T&&) = delete;

>     template<typename U, typename T>
>     unspecified
>     const_pointer_cast(T&&) = delete;

>     //Overloads for raw pointers
>     template<typename U, typename T>
>     auto static_pointer_cast(T* t) -> decltype(static_cast<U*>(t));

>     template<typename U, typename T>
>     auto dynamic_pointer_cast(T* t) -> decltype(dynamic_cast<U*>(t));

>     template<typename U, typename T>
>     auto const_pointer_cast(T* t) -> decltype(const_cast<U*>(t));

Add to section 20.2 [utility] Utility components, a new subclause 20.3.X Pointer cast utilities [pointer.cast]:

> 20.3.X Pointer cast utilities [pointer.cast]

> 1 The library defines generic pointer casting function templates so that template code can explicitly make these names visible and activate argument-dependent lookup for pointer cast calls.

>     //Generic declarations
>     template<typename U, typename T>
>     unspecified
>     static_pointer_cast(T&&) = delete;

>     template<typename U, typename T>
>     unspecified
>     dynamic_pointer_cast(T&&) = delete;

>     template<typename U, typename T>
>     unspecified
>     const_pointer_cast(T&&) = delete;

> 2 The library also defines overloads of these functions for raw pointers.

>     //Overloads for raw pointers
>     template<typename U, typename T>
>     auto static_pointer_cast(T* t) -> decltype(static_cast<U*>(t));

> > Returns: `static_cast<U*>(t)`

>     template<typename U, typename T>
>     auto dynamic_pointer_cast(T* t) -> decltype(dynamic_cast<U*>(t));

> > Returns: `dynamic_cast<U*>(t)`

>     template<typename U, typename T>
>     auto const_pointer_cast(T* t) -> decltype(const_cast<U*>(t));

> > Returns: `const_cast<U*>(t)`

> [Example:

> >     #include <utility> //static_pointer_cast
> >     #include <memory>  //pointer_traits

> >     class Base{};
> >     class Derived : public Base{};

> >     template<class BasePtr>
> >     void generic_pointer_code(BasePtr b)
> >     {
> >        typedef std::pointer_traits<BasePtr>::template
> >           rebind<Derived> DerivedPtr;

> >        using std::static_pointer_cast;
> >        //ADL applies now that static_pointer_cast is visible
> >        DerivedPtr d = static_pointer_cast<Derived>(b);
> >     }

> — end example]

Replace in section 16.4.4.6 [allocator.requirements] Table 40 — Allocator requirements, the following table entries for allocator pointers:

> Table 40 — Allocator requirements
> Expression | Return type | Assertion/note pre-/post-condition | Default
> --- | --- | --- | ---
> `static_pointer_cast<X::pointerT>(w)` | `X::pointer` | `static_pointer_cast<X::pointerT>(w) == p` |
> `static_pointer_cast<X::const_pointerconst T>(w)` | `X::const_pointer` | `static_pointer_cast<X::const_pointerconst T>(z) == q` |