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]

В shared_­ptr шаблоне класса хранится указатель, обычно получаемый через new. shared_­ptr реализует семантику долевого владения; последний оставшийся владелец указателя отвечает за уничтожение объекта или иное освобождение ресурсов, связанных с сохраненным указателем. А shared_­ptr называется пустым, если ему не принадлежит указатель.

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;
    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);
    constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }

    // [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>;

  // [util.smartptr.shared.create], shared_­ptr creation
  template<class T, class... Args>
    shared_ptr<T> make_shared(Args&&... args);
  template<class T, class A, class... Args>
    shared_ptr<T> allocate_shared(const A& a, Args&&... args);

  // [util.smartptr.shared.cmp], shared_­ptr comparisons
  template<class T, class U>
    bool operator==(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
  template<class T, class U>
    bool operator!=(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
  template<class T, class U>
    bool operator<(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
  template<class T, class U>
    bool operator>(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
  template<class T, class U>
    bool operator<=(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;
  template<class T, class U>
    bool operator>=(const shared_ptr<T>& a, const shared_ptr<U>& b) noexcept;

  template <class T>
    bool operator==(const shared_ptr<T>& a, nullptr_t) noexcept;
  template <class T>
    bool operator==(nullptr_t, const shared_ptr<T>& b) noexcept;
  template <class T>
    bool operator!=(const shared_ptr<T>& a, nullptr_t) noexcept;
  template <class T>
    bool operator!=(nullptr_t, const shared_ptr<T>& b) noexcept;
  template <class T>
    bool operator<(const shared_ptr<T>& a, nullptr_t) noexcept;
  template <class T>
    bool operator<(nullptr_t, const shared_ptr<T>& b) noexcept;
  template <class T>
    bool operator<=(const shared_ptr<T>& a, nullptr_t) noexcept;
  template <class T>
    bool operator<=(nullptr_t, const shared_ptr<T>& b) noexcept;
  template <class T>
    bool operator>(const shared_ptr<T>& a, nullptr_t) noexcept;
  template <class T>
    bool operator>(nullptr_t, const shared_ptr<T>& b) noexcept;
  template <class T>
    bool operator>=(const shared_ptr<T>& a, nullptr_t) noexcept;
  template <class T>
    bool operator>=(nullptr_t, const shared_ptr<T>& b) noexcept;

  // [util.smartptr.shared.spec], shared_­ptr specialized algorithms
  template<class T>
    void swap(shared_ptr<T>& a, shared_ptr<T>& b) noexcept;

  // [util.smartptr.shared.cast], shared_­ptr casts
  template<class T, class U>
    shared_ptr<T> static_pointer_cast(const shared_ptr<U>& r) noexcept;
  template<class T, class U>
    shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;
  template<class T, class U>
    shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;
  template<class T, class U>
    shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;

  // [util.smartptr.getdeleter], shared_­ptr get_­deleter
  template<class D, class T>
    D* get_deleter(const shared_ptr<T>& p) noexcept;

  // [util.smartptr.shared.io], shared_­ptr I/O
  template<class E, class T, class Y>
    basic_ostream<E, T>& operator<< (basic_ostream<E, T>& os, const shared_ptr<Y>& p);
}

Специализации shared_­ptr должны быть CopyConstructible, CopyAssignableи LessThanComparable, позволяющие использовать их в стандартных контейнерах. Специализации shared_­ptr должны быть контекстно преобразованы в bool, позволяя использовать их в логических выражениях и объявлениях в условиях. Параметр шаблона T из shared_­ptr может быть неполным типом.

[Example:

if (shared_ptr<X> px = dynamic_pointer_cast<X>(py)) {
  // do something with px
}

end example]

Для целей определения присутствия расы данных, функция - членов должна получать доступ и изменять только shared_­ptr и weak_­ptr сами объекты , а не объекты , они относятся. Изменения в use_­count() не отражают модификации, которые могут привести к гонке данных.

Для целей подпункта [util.smartptr]тип указателя Y* называется compatible with типом указателя, T* когда либо он Y* может быть преобразован в, T* либо Y является U[N] и T есть cv U[].

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

В определениях конструктора, приведенных ниже, включение shared_­from_­this with pдля указателя p типа Y*означает, что при Y наличии однозначного и доступного базового класса, который является специализацией enable_­shared_­from_­this, то он remove_­cv_­t<Y>* должен быть неявно преобразован в, T* и конструктор оценивает оператор:

if (p != nullptr && p->weak_this.expired())
  p->weak_this = shared_ptr<remove_cv_t<Y>>(*this, const_cast<remove_cv_t<Y>*>(p));

Присвоение weak_­this члену не является атомарным и конфликтует с любым потенциально одновременным доступом к тому же объекту ([intro.multithread]).

constexpr shared_ptr() noexcept;

Effects: Создает пустой shared_­ptr объект.

Postconditions: use_­count() == 0 && get() == nullptr.

template<class Y> explicit shared_ptr(Y* p);

Requires: Y должен быть законченным типом. Выражение delete[] p, когда T является типом массива или delete p, когда T не является типом массива, должно иметь четко определенное поведение и не должно вызывать исключений.

Effects: Когда T не является типом массива, создает shared_­ptr объект, которому принадлежит указатель p. В противном случае создает объект- shared_­ptr владелец p и вызывающий объект удаления неопределенного типа delete[] p. Когда T не является типом массива, включается shared_­from_­this с помощью p. Если выбрасывается исключение, вызывается, delete p если T он не является типом массива, в delete[] p противном случае.

Postconditions: use_­count() == 1 && get() == p.

Throws: bad_­allocили исключение, определяемое реализацией, когда не удалось получить ресурс, отличный от памяти.

Remarks: Когда T является типом массива, этот конструктор не должен участвовать в разрешении перегрузки, если выражение не delete[] p имеет правильного формата и либо T является U[N] и Y(*)[N] может быть преобразовано в T*, либо T является U[] и Y(*)[] может быть преобразовано в T*. Когда T не является типом массива, этот конструктор не должен участвовать в разрешении перегрузки, если выражение не delete p имеет правильного формата и не Y* может быть преобразовано в 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: Создание d и удаление типа, D инициализированного с помощью std​::​move(d) , не должны вызывать исключений. Выражение d(p) должно иметь четко определенное поведение и не должно вызывать исключений. A должен быть распределителем ([allocator.requirements]).

Effects: Создает shared_­ptr объект, которому принадлежит объект p и средство удаления d. Когда T это не массив, первый и второй конструкторы активируются shared_­from_­this с помощью p. Второй и четвертый конструкторы должны использовать копию a для выделения памяти для внутреннего использования. Если выбрасывается исключение, d(p) вызывается.

Postconditions: use_­count() == 1 && get() == p.

Throws: bad_­allocили исключение, определяемое реализацией, когда не удалось получить ресурс, отличный от памяти.

Remarks: Когда T это тип массива, этот конструктор не будет участвовать в разрешении перегрузки , если is_­move_­constructible_­v<D> нет true, то выражение d(p) хорошо сформирован, и либо T это U[N] и Y(*)[N] превращена в T*или T вне U[] и Y(*)[] конвертируется в T*. Когда T это не тип массива, этот конструктор не должен участвовать в разрешении перегрузки , если is_­move_­constructible_­v<D> не trueвыражение d(p) хорошо сформировано, и Y* конвертируются в T*.

template<class Y> shared_ptr(const shared_ptr<Y>& r, element_type* p) noexcept;

Effects: Создает shared_­ptr экземпляр, который хранит p и разделяет владение r.

Postconditions: get() == p && use_­count() == r.use_­count().

[ Note: Чтобы избежать возможности появления висячего указателя, пользователь этого конструктора должен убедиться, что он p остается действительным, по крайней мере, до тех пор, пока группа владения не r будет уничтожена. ] end note

[ Note: Этот конструктор позволяет создавать пустой shared_­ptr экземпляр с сохраненным указателем, отличным от NULL. ] end note

shared_ptr(const shared_ptr& r) noexcept; template<class Y> shared_ptr(const shared_ptr<Y>& r) noexcept;

Remarks: Второй конструктор не должен участвовать в разрешении перегрузки, если он Y* не совместим с T*.

Effects: Если r пусто, создает пустой shared_­ptr объект; в противном случае создает shared_­ptr объект, с которым совместно владеет 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: Второй конструктор не должен участвовать в разрешении перегрузки, если он Y* не совместим с T*.

Effects: Move создает shared_­ptr экземпляр из r.

Postconditions: *this должен содержать старое значение r. r будет пустым. r.get() == nullptr.

template<class Y> explicit shared_ptr(const weak_ptr<Y>& r);

Effects: Создает shared_­ptr объект, который разделяет владение r и хранит копию указателя, хранящегося в r. Если выбрасывается исключение, конструктор не действует.

Postconditions: use_­count() == r.use_­count().

Throws: bad_­weak_­ptr когда r.expired().

Remarks: Этот конструктор не должен участвовать в разрешении перегрузки, если он Y* не совместим с T*.

template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r);

Remarks: Этот конструктор не должен участвовать в разрешении перегрузки , если Y* не совместим с T* и unique_­ptr<Y, D>​::​pointer конвертируется в element_­type*.

Effects: Если r.get() == nullptrэквивалентно shared_­ptr(). В противном случае, если D это не ссылочный тип, эквивалентный shared_­ptr(r.release(), r.get_­deleter()). В противном случае эквивалентно shared_­ptr(r.release(), ref(r.get_­deleter())). Если выбрасывается исключение, конструктор не действует.

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

~shared_ptr();

Effects:

  • Если *this он пуст или принадлежит другому shared_­ptr экземпляру (use_­count() > 1), побочных эффектов нет.

  • В противном случае, если *this владелец объекта p и Deleter d, d(p) называется.

  • В противном случае *this владеет указателем pи delete p вызывается.

[ Note: Так как уничтожение *this уменьшает количество экземпляров, которые разделяют владение, *this на единицу, после того, как *this было уничтожено, все shared_­ptr экземпляры, с которыми совместно владеют, *this будут сообщать, use_­count() что на единицу меньше его предыдущего значения. ] 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: Эквивалентно shared_­ptr(r).swap(*this).

Returns: *this.

[ Note: Обновления счетчика использования, вызванные созданием и разрушением временного объекта, не являются наблюдаемыми побочными эффектами, поэтому реализация может соответствовать эффектам (и подразумеваемым гарантиям) с помощью различных средств, без создания временного. В частности, в примере:

shared_ptr<int> p(new int);
shared_ptr<void> q(p);
p = p;
q = p;

оба назначения могут быть запретными. ] end note

shared_ptr& operator=(shared_ptr&& r) noexcept; template<class Y> shared_ptr& operator=(shared_ptr<Y>&& r) noexcept;

Effects: Эквивалентно shared_­ptr(std​::​move(r)).swap(*this).

Returns: *this.

template <class Y, class D> shared_ptr& operator=(unique_ptr<Y, D>&& r);

Effects: Эквивалентно 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: Меняет содержимое *this и r.

void reset() noexcept;

Effects: Эквивалентно shared_­ptr().swap(*this).

template<class Y> void reset(Y* p);

Effects: Эквивалентно shared_­ptr(p).swap(*this).

template<class Y, class D> void reset(Y* p, D d);

Effects: Эквивалентно shared_­ptr(p, d).swap(*this).

template<class Y, class D, class A> void reset(Y* p, D d, A a);

Effects: Эквивалентно 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: Сохраненный указатель.

T& operator*() const noexcept;

Requires: get() != 0.

Returns: *get().

Remarks: Когда T является типом массива или cv void, не указано, объявлена ​​ли эта функция-член. Если он объявлен, то не указывается, каков его возвращаемый тип, за исключением того, что объявление (хотя и не обязательно определение) функции должно быть правильно сформировано.

T* operator->() const noexcept;

Requires: get() != 0.

Returns: get().

Remarks: Когда T это тип массива, не указывается, объявлена ​​ли эта функция-член. Если он объявлен, то не указывается, каков его возвращаемый тип, за исключением того, что объявление (хотя и не обязательно определение) функции должно быть правильно сформировано.

element_type& operator[](ptrdiff_t i) const;

Requires: get() != 0 && i >= 0. Если T есть U[N], то i < N.

Returns: get()[i].

Remarks: Когда T не является типом массива, не указывается, объявлена ​​ли эта функция-член. Если он объявлен, то не указывается, каков его возвращаемый тип, за исключением того, что объявление (хотя и не обязательно определение) функции должно быть правильно сформировано.

Throws: Ничего такого.

long use_count() const noexcept;

Returns: Количество включенных shared_­ptr объектов, *this которым принадлежит общая собственность *this, или 0 когда *this оно пусто.

Synchronization: Никто.

[ Note: get() == nullptr не подразумевает конкретного возвращаемого значения use_­count(). ] end note

[ Note: weak_­ptr<T>​::​lock() может повлиять на возвращаемое значение use_­count(). ] end note

[ Note: Если несколько потоков могут повлиять на возвращаемое значение use_­count(), результат следует рассматривать как приблизительный. В частности, use_­count() == 1 это не означает, что доступ через ранее уничтоженный объект shared_­ptr в каком-либо смысле завершен. ] 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: Неуказанное значение такое, что

  • x.owner_­before(y) определяет строгий слабый порядок, как определено в [alg.sorting];

  • по отношению эквивалентности , определенное owner_­before, !a.owner_­before(b) && !b.owner_­before(a)два shared_­ptr или weak_­ptr экземпляры эквивалентны тогда и только тогда , когда они имеют право собственности или являются пустыми.

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

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);

Requires: Выражение ​::​new (pv) T(std​::​forward<Args>(args)...), где pv имеет тип void* и указывает на хранилище, подходящее для хранения объекта типа T, должно быть правильно сформировано. A должен быть allocator. Конструктор копирования и деструктор A не должны вызывать исключений.

Effects: Выделяет память, подходящую для объекта типа, T и создает объект в этой памяти через размещение new-expression ​::​new (pv) T(std​::​forward<Args>(args)...). В шаблоне для выделения памяти allocate_­shared используется копия a . Если выбрасывается исключение, функции не действуют.

Returns: shared_­ptr Экземпляр , который хранит и владеет адрес создаваемого объекта типа T.

Postconditions: get() != 0 && use_­count() == 1.

Throws: bad_­allocили исключение, A​::​allocate созданное конструктором T.

Remarks: shared_­ptr Конструктор вызывается этой функции позволяет shared_­from_­this с адресом создаваемого объекта типа T. Реализации должны выполнять не более одного распределения памяти. [ Note: Это обеспечивает эффективность, эквивалентную навязчивому интеллектуальному указателю. ] end note

[ Note: Эти функции обычно выделяют больше памяти, чем sizeof(T) позволяют внутренние структуры бухгалтерского учета, такие как счетчик ссылок. ] end note

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: Определение функции сравнения позволяет использовать shared_­ptr объекты в качестве ключей в ассоциативных контейнерах. ] 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: Первый шаблон функции возвращается less<shared_­ptr<T>​::​element_­type*>()(a.get(), nullptr). Второй шаблон функции возвращается less<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: Первый шаблон функции возвращается nullptr < a. Второй шаблон функции возвращается 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: Первый шаблон функции возвращается !(nullptr < a). Второй шаблон функции возвращается !(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: Первый шаблон функции возвращается !(a < nullptr). Второй шаблон функции возвращается !(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: Эквивалентно 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: Выражение static_­cast<T*>((U*)0) должно быть правильным.

Returns:

shared_ptr<T>(r, static_cast<typename shared_ptr<T>::element_type*>(r.get()))

[ Note: Казалось бы, эквивалентное выражение в shared_­ptr<T>(static_­cast<T*>(r.get())) конечном итоге приведет к неопределенному поведению, пытаясь дважды удалить один и тот же объект. ] end note

template<class T, class U> shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U>& r) noexcept;

Requires: Выражение dynamic_­cast<T*>((U*)0) должно быть правильно сформировано и иметь четко определенное поведение.

Returns:

  • Когда dynamic_­cast<typename shared_­ptr<T>​::​element_­type*>(r.get()) возвращает значение отличное от нуля p, shared_­ptr<T>(r, p).

  • В противном случае shared_­ptr<T>().

[ Note: Казалось бы, эквивалентное выражение в shared_­ptr<T>(dynamic_­cast<T*>(r.get())) конечном итоге приведет к неопределенному поведению, пытаясь дважды удалить один и тот же объект. ] end note

template<class T, class U> shared_ptr<T> const_pointer_cast(const shared_ptr<U>& r) noexcept;

Requires: Выражение const_­cast<T*>((U*)0) должно быть правильным.

Returns:

shared_ptr<T>(r, const_cast<typename shared_ptr<T>::element_type*>(r.get()))

[ Note: Казалось бы, эквивалентное выражение в shared_­ptr<T>(const_­cast<T*>(r.get())) конечном итоге приведет к неопределенному поведению, пытаясь дважды удалить один и тот же объект. ] end note

template<class T, class U> shared_ptr<T> reinterpret_pointer_cast(const shared_ptr<U>& r) noexcept;

Requires: Выражение reinterpret_­cast<T*>((U*)0) должно быть правильным.

Returns:

shared_ptr<T>(r, reinterpret_cast<typename shared_ptr<T>::element_type*>(r.get()))

[ Note: Казалось бы, эквивалентное выражение в shared_­ptr<T>(reinterpret_­cast<T*>(r.get())) конечном итоге приведет к неопределенному поведению, пытаясь дважды удалить один и тот же объект. ] 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: Если p владеет средством удаления d типа cv-unqualified D, возвращается addressof(d); в противном случае возвращается nullptr. Возвращенный указатель остается действительным, пока существует shared_­ptr экземпляр, которому он принадлежит d. [ Note: Не указано, остается ли указатель действительным дольше этого срока. Это может произойти, если реализация не уничтожит средство удаления до тех пор, пока не будут уничтожены все weak_­ptr экземпляры, которые совместноp владеют правами собственности . ] 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: Как будто по: os << p.get();

Returns: os.