Section: 22.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-conditionDefault 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.3.2.2.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 22.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 22.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-conditionDefault static_pointer_cast<
X::pointerT>(w)X::pointer
static_pointer_cast<
X::pointerT>(w) == pstatic_pointer_cast<
X::const_pointerconst T>(w)X::const_pointer
static_pointer_cast<
X::const_pointerconst T>(z) == q