23 General utilities library [utilities]

23.11 Smart pointers [smartptr]

23.11.2 Shared-ownership pointers [util.smartptr]

23.11.2.2 Class template shared_­ptr [util.smartptr.shared]

The shared_­ptr class template stores a pointer, usually obtained via new.
shared_­ptr implements semantics of shared ownership; the last remaining owner of the pointer is responsible for destroying the object, or otherwise releasing the resources associated with the stored pointer.
A shared_­ptr is said to be empty if it does not own a pointer.
namespace std {
  template <class T> class shared_ptr {
  public:
    using element_type = remove_extent_t<T>;
    using weak_type    = weak_ptr<T>;

    // [util.smartptr.shared.const], constructors
    constexpr shared_ptr() noexcept;
    constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
    template <class Y>
      explicit shared_ptr(Y* p);
    template <class Y, class D>
      shared_ptr(Y* p, D d);
    template <class Y, class D, class A>
      shared_ptr(Y* p, D d, A a);
    template <class D>
      shared_ptr(nullptr_t p, D d);
    template <class D, class A>
      shared_ptr(nullptr_t p, D d, A a);
    template <class Y>
      shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
    shared_ptr(const shared_ptr& r) noexcept;
    template <class Y>
      shared_ptr(const shared_ptr<Y>& r) noexcept;
    shared_ptr(shared_ptr&& r) noexcept;
    template <class Y>
      shared_ptr(shared_ptr<Y>&& r) noexcept;
    template <class Y>
      explicit shared_ptr(const weak_ptr<Y>& r);
    template <class Y, class D>
      shared_ptr(unique_ptr<Y, D>&& r);

    // [util.smartptr.shared.dest], destructor
    ~shared_ptr();

    // [util.smartptr.shared.assign], assignment
    shared_ptr& operator=(const shared_ptr& r) noexcept;
    template <class Y>
      shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
    shared_ptr& operator=(shared_ptr&& r) noexcept;
    template <class Y>
      shared_ptr& operator=(shared_ptr<Y>&& r) noexcept;
    template <class Y, class D>
      shared_ptr& operator=(unique_ptr<Y, D>&& r);

    // [util.smartptr.shared.mod], modifiers
    void swap(shared_ptr& r) noexcept;
    void reset() noexcept;
    template <class Y>
      void reset(Y* p);
    template <class Y, class D>
      void reset(Y* p, D d);
    template <class Y, class D, class A>
      void reset(Y* p, D d, A a);

    // [util.smartptr.shared.obs], observers
    element_type* get() const noexcept;
    T& operator*() const noexcept;
    T* operator->() const noexcept;
    element_type& operator[](ptrdiff_t i) const;
    long use_count() const noexcept;
    explicit operator bool() const noexcept;
    template <class U>
      bool owner_before(const shared_ptr<U>& b) const noexcept;
    template <class U>
      bool owner_before(const weak_ptr<U>& b) const noexcept;
  };

  template <class T>
    shared_ptr(weak_ptr<T>) -> shared_ptr<T>;
  template <class T, class D>
    shared_ptr(unique_ptr<T, D>) -> shared_ptr<T>;
}
Specializations of shared_­ptr shall be CopyConstructible, CopyAssignable, and LessThanComparable, allowing their use in standard containers.
Specializations of shared_­ptr shall be contextually convertible to bool, allowing their use in boolean expressions and declarations in conditions.
The template parameter T of shared_­ptr may be an incomplete type.
[Example
:
if (shared_ptr<X> px = dynamic_pointer_cast<X>(py)) {
  // do something with px
}
end example
]
For purposes of determining the presence of a data race, member functions shall access and modify only the shared_­ptr and weak_­ptr objects themselves and not objects they refer to.
Changes in use_­count() do not reflect modifications that can introduce data races.
For the purposes of subclause [util.smartptr], a pointer type Y* is said to be compatible with a pointer type T* when either Y* is convertible to T* or Y is U[N] and T is cv U[].

23.11.2.2.1 shared_­ptr constructors [util.smartptr.shared.const]

In the constructor definitions below, enables shared_­from_­this with p, for a pointer p of type Y*, means that if Y has an unambiguous and accessible base class that is a specialization of enable_­shared_­from_­this, then remove_­cv_­t<Y>* shall be implicitly convertible to T* and the constructor evaluates the statement:
if (p != nullptr && p->weak_this.expired())
  p->weak_this = shared_ptr<remove_cv_t<Y>>(*this, const_cast<remove_cv_t<Y>*>(p));
The assignment to the weak_­this member is not atomic and conflicts with any potentially concurrent access to the same object ([intro.multithread]).
constexpr shared_ptr() noexcept;
Effects: Constructs an empty shared_­ptr object.
Postconditions: use_­count() == 0 && get() == nullptr.
template <class Y> explicit shared_ptr(Y* p);
Requires: Y shall be a complete type.
The expression delete[] p, when T is an array type, or delete p, when T is not an array type, shall have well-defined behavior, and shall not throw exceptions.
Effects: When T is not an array type, constructs a shared_­ptr object that owns the pointer p.
Otherwise, constructs a shared_­ptr that owns p and a deleter of an unspecified type that calls delete[] p.
When T is not an array type, enables shared_­from_­this with p.
If an exception is thrown, delete p is called when T is not an array type, delete[] p otherwise.
Postconditions: use_­count() == 1 && get() == p.
Throws: bad_­alloc, or an implementation-defined exception when a resource other than memory could not be obtained.
Remarks: When T is an array type, this constructor shall not participate in overload resolution unless the expression delete[] p is well-formed and either T is U[N] and Y(*)[N] is convertible to T*, or T is U[] and Y(*)[] is convertible to T*.
When T is not an array type, this constructor shall not participate in overload resolution unless the expression delete p is well-formed and Y* is convertible to T*.
template <class Y, class D> shared_ptr(Y* p, D d); template <class Y, class D, class A> shared_ptr(Y* p, D d, A a); template <class D> shared_ptr(nullptr_t p, D d); template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
Requires: Construction of d and a deleter of type D initialized with std​::​move(d) shall not throw exceptions.
The expression d(p) shall have well-defined behavior and shall not throw exceptions.
A shall be an allocator ([allocator.requirements]).
Effects: Constructs a shared_­ptr object that owns the object p and the deleter d.
When T is not an array type, the first and second constructors enable shared_­from_­this with p.
The second and fourth constructors shall use a copy of a to allocate memory for internal use.
If an exception is thrown, d(p) is called.
Postconditions: use_­count() == 1 && get() == p.
Throws: bad_­alloc, or an implementation-defined exception when a resource other than memory could not be obtained.
Remarks: When T is an array type, this constructor shall not participate in overload resolution unless is_­move_­constructible_­v<D> is true, the expression d(p) is well-formed, and either T is U[N] and Y(*)[N] is convertible to T*, or T is U[] and Y(*)[] is convertible to T*.
When T is not an array type, this constructor shall not participate in overload resolution unless is_­move_­constructible_­v<D> is true, the expression d(p) is well-formed, and Y* is convertible to T*.
template <class Y> shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;
Effects: Constructs a shared_­ptr instance that stores p and shares ownership with r.
Postconditions: get() == p && use_­count() == r.use_­count().
[Note
:
To avoid the possibility of a dangling pointer, the user of this constructor should ensure that p remains valid at least until the ownership group of r is destroyed.
end note
]
[Note
:
This constructor allows creation of an empty shared_­ptr instance with a non-null stored pointer.
end note
]
shared_ptr(const shared_ptr& r) noexcept; template <class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;
Remarks: The second constructor shall not participate in overload resolution unless Y* is compatible with T*.
Effects: If r is empty, constructs an empty shared_­ptr object; otherwise, constructs a shared_­ptr object that shares ownership with r.
Postconditions: get() == r.get() && use_­count() == r.use_­count().
shared_ptr(shared_ptr&& r) noexcept; template <class Y> shared_ptr(shared_ptr<Y>&& r) noexcept;
Remarks: The second constructor shall not participate in overload resolution unless Y* is compatible with T*.
Effects: Move constructs a shared_­ptr instance from r.
Postconditions: *this shall contain the old value of r.
r shall be empty.
r.get() == nullptr.
template <class Y> explicit shared_ptr(const weak_ptr<Y>& r);
Effects: Constructs a shared_­ptr object that shares ownership with r and stores a copy of the pointer stored in r.
If an exception is thrown, the constructor has no effect.
Postconditions: use_­count() == r.use_­count().
Throws: bad_­weak_­ptr when r.expired().
Remarks: This constructor shall not participate in overload resolution unless Y* is compatible with T*.
template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);
Remarks: This constructor shall not participate in overload resolution unless Y* is compatible with T* and unique_­ptr<Y, D>​::​pointer is convertible to element_­type*.
Effects: If r.get() == nullptr, equivalent to shared_­ptr().
Otherwise, if D is not a reference type, equivalent to shared_­ptr(r.release(), r.get_­deleter()).
Otherwise, equivalent to shared_­ptr(r.release(), ref(r.get_­deleter())).
If an exception is thrown, the constructor has no effect.

23.11.2.2.2 shared_­ptr destructor [util.smartptr.shared.dest]

~shared_ptr();
Effects:
  • If *this is empty or shares ownership with another shared_­ptr instance (use_­count() > 1), there are no side effects.
  • Otherwise, if *this owns an object p and a deleter d, d(p) is called.
  • Otherwise, *this owns a pointer p, and delete p is called.
[Note
:
Since the destruction of *this decreases the number of instances that share ownership with *this by one, after *this has been destroyed all shared_­ptr instances that shared ownership with *this will report a use_­count() that is one less than its previous value.
end note
]

23.11.2.2.3 shared_­ptr assignment [util.smartptr.shared.assign]

shared_ptr& operator=(const shared_ptr& r) noexcept; template <class Y> shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
Effects: Equivalent to shared_­ptr(r).swap(*this).
Returns: *this.
[Note
:
The use count updates caused by the temporary object construction and destruction are not observable side effects, so the implementation may meet the effects (and the implied guarantees) via different means, without creating a temporary.
In particular, in the example:
shared_ptr<int> p(new int);
shared_ptr<void> q(p);
p = p;
q = p;
both assignments may be no-ops.
end note
]
shared_ptr& operator=(shared_ptr&& r) noexcept; template <class Y> shared_ptr& operator=(shared_ptr<Y>&& r) noexcept;
Effects: Equivalent to shared_­ptr(std​::​move(r)).swap(*this).
Returns: *this.
template <class Y, class D> shared_ptr& operator=(unique_ptr<Y, D>&& r);
Effects: Equivalent to shared_­ptr(std​::​move(r)).swap(*this).
Returns: *this.

23.11.2.2.4 shared_­ptr modifiers [util.smartptr.shared.mod]

void swap(shared_ptr& r) noexcept;
Effects: Exchanges the contents of *this and r.
void reset() noexcept;
Effects: Equivalent to shared_­ptr().swap(*this).
template <class Y> void reset(Y* p);
Effects: Equivalent to shared_­ptr(p).swap(*this).
template <class Y, class D> void reset(Y* p, D d);
Effects: Equivalent to shared_­ptr(p, d).swap(*this).
template <class Y, class D, class A> void reset(Y* p, D d, A a);
Effects: Equivalent to shared_­ptr(p, d, a).swap(*this).

23.11.2.2.5 shared_­ptr observers [util.smartptr.shared.obs]

element_type* get() const noexcept;
Returns: The stored pointer.
T& operator*() const noexcept;
Requires: get() != 0.
Returns: *get().
Remarks: When T is an array type or cv void, it is unspecified whether this member function is declared.
If it is declared, it is unspecified what its return type is, except that the declaration (although not necessarily the definition) of the function shall be well-formed.
T* operator->() const noexcept;
Requires: get() != 0.
Returns: get().
Remarks: When T is an array type, it is unspecified whether this member function is declared.
If it is declared, it is unspecified what its return type is, except that the declaration (although not necessarily the definition) of the function shall be well-formed.
element_type& operator[](ptrdiff_t i) const;
Requires: get() != 0 && i >= 0.
If T is U[N], i < N.
Returns: get()[i].
Remarks: When T is not an array type, it is unspecified whether this member function is declared.
If it is declared, it is unspecified what its return type is, except that the declaration (although not necessarily the definition) of the function shall be well-formed.
Throws: Nothing.
long use_count() const noexcept;
Returns: The number of shared_­ptr objects, *this included, that share ownership with *this, or 0 when *this is empty.
Synchronization: None.
[Note
:
get() == nullptr does not imply a specific return value of use_­count().
end note
]
[Note
:
weak_­ptr<T>​::​lock() can affect the return value of use_­count().
end note
]
[Note
:
When multiple threads can affect the return value of use_­count(), the result should be treated as approximate.
In particular, use_­count() == 1 does not imply that accesses through a previously destroyed shared_­ptr have in any sense completed.
end note
]
explicit operator bool() const noexcept;
Returns: get() != 0.
template <class U> bool owner_before(const shared_ptr<U>& b) const noexcept; template <class U> bool owner_before(const weak_ptr<U>& b) const noexcept;
Returns: An unspecified value such that
  • x.owner_­before(y) defines a strict weak ordering as defined in [alg.sorting];
  • under the equivalence relation defined by owner_­before, !a.owner_­before(b) && !b.owner_­before(a), two shared_­ptr or weak_­ptr instances are equivalent if and only if they share ownership or are both empty.

23.11.2.2.6 shared_­ptr creation [util.smartptr.shared.create]

The common requirements that apply to all make_­shared and allocate_­shared overloads, unless specified otherwise, are described below.
template <class T, ...> shared_ptr<T> make_­shared(args); template <class T, class A, ...> shared_ptr<T> allocate_­shared(const A& a, args);
Requires: A shall be an allocator.
Effects: Allocates memory for an object of type T (or U[N] when T is U[], where N is determined from args as specified by the concrete overload).
The object is initialized from args as specified by the concrete overload.
The allocate_­shared templates use a copy of a (rebound for an unspecified value_­type) to allocate memory.
If an exception is thrown, the functions have no effect.
Returns: A shared_­ptr instance that stores and owns the address of the newly constructed object.
Postconditions: r.get() != 0 && r.use_­count() == 1, where r is the return value.
Throws: bad_­alloc, or an exception thrown from allocate or from the initialization of the object.
Remarks:
  • Implementations should perform no more than one memory allocation.
    [Note
    :
    This provides efficiency equivalent to an intrusive smart pointer.
    end note
    ]
  • When an object of an array type U is specified to have an initial value of u (of the same type), this shall be interpreted to mean that each array element of the object has as its initial value the corresponding element from u.
  • When an object of an array type is specified to have a default initial value, this shall be interpreted to mean that each array element of the object has a default initial value.
  • When a (sub)object of a non-array type U is specified to have an initial value of v, or U(l...), where l... is a list of constructor arguments, make_­shared shall initialize this (sub)object via the expression ​::​new(pv) U(v) or ​::​new(pv) U(l...) respectively, where pv has type void* and points to storage suitable to hold an object of type U.
  • When a (sub)object of a non-array type U is specified to have an initial value of v, or U(l...), where l... is a list of constructor arguments, allocate_­shared shall initialize this (sub)object via the expression
    • allocator_­traits<A2>​::​construct(a2, pv, v) or
    • allocator_­traits<A2>​::​construct(a2, pv, l...)
    respectively, where pv points to storage suitable to hold an object of type U and a2 of type A2 is a rebound copy of the allocator a passed to allocate_­shared such that its value_­type is U.
  • When a (sub)object of non-array type U is specified to have a default initial value, make_­shared shall initialize this (sub)object via the expression ​::​new(pv) U(), where pv has type void* and points to storage suitable to hold an object of type U.
  • When a (sub)object of non-array type U is specified to have a default initial value, allocate_­shared shall initialize this (sub)object via the expression allocator_­traits<A2>​::​construct(a2, pv), where pv points to storage suitable to hold an object of type U and a2 of type A2 is a rebound copy of the allocator a passed to allocate_­shared such that its value_­type is U.
  • Array elements are initialized in ascending order of their addresses.
  • When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements should be destroyed in the reverse order of their construction.
[Note
:
These functions will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as reference counts.
end note
]
template <class T, class... Args> shared_ptr<T> make_­shared(Args&&... args); // T is not array template <class T, class A, class... Args> shared_ptr<T> allocate_­shared(const A& a, Args&&... args); // T is not array
Returns: A shared_­ptr to an object of type T with an initial value T(forward<Args>(args)...).
Remarks: These overloads shall only participate in overload resolution when T is not an array type.
The shared_­ptr constructors called by these functions enable shared_­from_­this with the address of the newly constructed object of type T.
[Example
:
shared_ptr<int> p = make_shared<int>(); // shared_­ptr to int()
shared_ptr<vector<int>> q = make_shared<vector<int>>(16, 1);
  // shared_­ptr to vector of 16 elements with value 1
end example
]
template <class T> shared_ptr<T> make_shared(size_t N); // T is U[] template <class T, class A> shared_ptr<T> allocate_shared(const A& a, size_t N); // T is U[]
Returns: A shared_­ptr to an object of type U[N] with a default initial value, where U is remove_­extent_­t<T>.
Remarks: These overloads shall only participate in overload resolution when T is of the form U[].
[Example
:
shared_ptr<double[]> p = make_shared<double[]>(1024);
  // shared_­ptr to a value-initialized double[1024]
shared_ptr<double[][2][2]> q = make_shared<double[][2][2]>(6);
  // shared_­ptr to a value-initialized double[6][2][2]
end example
]
template <class T> shared_ptr<T> make_shared(); // T is U[N] template <class T, class A> shared_ptr<T> allocate_shared(const A& a); // T is U[N]
Returns: A shared_­ptr to an object of type T with a default initial value.
Remarks: These overloads shall only participate in overload resolution when T is of the form U[N].
[Example
:
shared_ptr<double[1024]> p = make_shared<double[1024]>();
  // shared_­ptr to a value-initialized double[1024]
shared_ptr<double[6][2][2]> q = make_shared<double[6][2][2]>();
  // shared_­ptr to a value-initialized double[6][2][2]
end example
]
template<class T> shared_ptr<T> make_shared(size_t N, const remove_extent_t<T>& u); // T is U[] template<class T, class A> shared_ptr<T> allocate_shared(const A& a, size_t N, const remove_extent_t<T>& u); // T is U[]
Returns: A shared_­ptr to an object of type U[N], where U is remove_­extent_­t<T> and each array element has an initial value of u.
Remarks: These overloads shall only participate in overload resolution when T is of the form U[].
[Example
:
shared_ptr<double[]> p = make_shared<double[]>(1024, 1.0);
  // shared_­ptr to a double[1024], where each element is 1.0
shared_ptr<double[][2]> q = make_shared<double[][2]>(6, {1.0, 0.0});
  // shared_­ptr to a double[6][2], where each double[2] element is {1.0, 0.0}
shared_ptr<vector<int>[]> r = make_shared<vector<int>[]>(4, {1, 2});
  // shared_­ptr to a vector<int>[4], where each vector has contents {1, 2}
end example
]
template<class T> shared_ptr<T> make_shared(const remove_extent_t<T>& u); // T is U[N] template<class T, class A> shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& u); // T is U[N]
Returns: A shared_­ptr to an object of type T, where each array element of type remove_­extent_­t<T> has an initial value of u.
Remarks: These overloads shall only participate in overload resolution when T is of the form U[N].
[Example
:
shared_ptr<double[1024]> p = make_shared<double[1024]>(1.0);
  // shared_­ptr to a double[1024], where each element is 1.0
shared_ptr<double[6][2]> q = make_shared<double[6][2]>({1.0, 0.0});
  // shared_­ptr to a double[6][2], where each double[2] element is {1.0, 0.0}
shared_ptr<vector<int>[4]> r = make_shared<vector<int>[4]>({1, 2});
  // shared_­ptr to a vector<int>[4], where each vector has contents {1, 2}
end example
]

23.11.2.2.7 shared_­ptr comparison [util.smartptr.shared.cmp]

template <class T, class U> bool operator==(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
Returns: a.get() == b.get().
template <class T, class U> bool operator<(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
Returns: less<>()(a.get(), b.get()).
[Note
:
Defining a comparison function allows shared_­ptr objects to be used as keys in associative containers.
end note
]
template <class T> bool operator==(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator==(nullptr_t, const shared_ptr<T>& a) noexcept;
Returns: !a.
template <class T> bool operator!=(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator!=(nullptr_t, const shared_ptr<T>& a) noexcept;
Returns: (bool)a.
template <class T> bool operator<(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator<(nullptr_t, const shared_ptr<T>& a) noexcept;
Returns: The first function template returns
less<typename shared_ptr<T>::element_type*>()(a.get(), nullptr)
The second function template returns
less<typename shared_ptr<T>::element_type*>()(nullptr, a.get())
template <class T> bool operator>(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator>(nullptr_t, const shared_ptr<T>& a) noexcept;
Returns: The first function template returns nullptr < a.
The second function template returns a < nullptr.
template <class T> bool operator<=(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator<=(nullptr_t, const shared_ptr<T>& a) noexcept;
Returns: The first function template returns !(nullptr < a).
The second function template returns !(a < nullptr).
template <class T> bool operator>=(const shared_ptr<T>& a, nullptr_t) noexcept; template <class T> bool operator>=(nullptr_t, const shared_ptr<T>& a) noexcept;
Returns: The first function template returns !(a < nullptr).
The second function template returns !(nullptr < a).

23.11.2.2.8 shared_­ptr specialized algorithms [util.smartptr.shared.spec]

template <class T> void swap(shared_ptr<T>& a, shared_ptr<T>& b) noexcept;
Effects: Equivalent to a.swap(b).

23.11.2.2.9 shared_­ptr casts [util.smartptr.shared.cast]

template <class T, class U> shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r) noexcept;
Requires: The expression static_­cast<T*>((U*)nullptr) shall be well-formed.
Returns:
shared_ptr<T>(r, static_cast<typename shared_ptr<T>::element_type*>(r.get()))
[Note
:
The seemingly equivalent expression shared_­ptr<T>(static_­cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice.
end note
]
template <class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;
Requires: The expression dynamic_­cast<T*>((U*)nullptr) shall be well-formed and shall have well-defined behavior.
Returns:
  • When dynamic_­cast<typename shared_­ptr<T>​::​element_­type*>(r.get()) returns a non-null value p, shared_­ptr<T>(r, p).
  • Otherwise, shared_­ptr<T>().
[Note
:
The seemingly equivalent expression shared_­ptr<T>(dynamic_­cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice.
end note
]
template <class T, class U> shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;
Requires: The expression const_­cast<T*>((U*)nullptr) shall be well-formed.
Returns:
shared_ptr<T>(r, const_cast<typename shared_ptr<T>::element_type*>(r.get()))
[Note
:
The seemingly equivalent expression shared_­ptr<T>(const_­cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice.
end note
]
template <class T, class U> shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;
Requires: The expression reinterpret_­cast<T*>((U*)nullptr) shall be well-formed.
Returns:
shared_ptr<T>(r, reinterpret_cast<typename shared_ptr<T>::element_type*>(r.get()))
[Note
:
The seemingly equivalent expression shared_­ptr<T>(reinterpret_­cast<T*>(r.get())) will eventually result in undefined behavior, attempting to delete the same object twice.
end note
]

23.11.2.2.10 get_­deleter [util.smartptr.getdeleter]

template <class D, class T> D* get_deleter(const shared_ptr<T>& p) noexcept;
Returns: If p owns a deleter d of type cv-unqualified D, returns addressof(d); otherwise returns nullptr.
The returned pointer remains valid as long as there exists a shared_­ptr instance that owns d.
[Note
:
It is unspecified whether the pointer remains valid longer than that.
This can happen if the implementation doesn't destroy the deleter until all weak_­ptr instances that share ownership with p have been destroyed.
end note
]

23.11.2.2.11 shared_­ptr I/O [util.smartptr.shared.io]

template <class E, class T, class Y> basic_ostream<E, T>& operator<<(basic_ostream<E, T>& os, const shared_ptr<Y>& p);
Effects: As if by: os << p.get();
Returns: os.