Document numberP0414R2
Date2016-11-10
ProjectProgramming Language C++, Library Working Group
Reply-toJonathan Wakely <cxx@kayari.org>

Merging shared_ptr changes from Library Fundamentals to C++17

Revision History

Changes since P0414R1, following feedback by Tim Song:

Changes since P0414R0, following LWG review in Chicago, and LWG telecon:

Introduction

LWG Motion 6 in Jacksonville was to apply to the C++ working paper the wording from P0220R1, Adopt Library Fundamentals V2 TS Components for C++17 (R1), but due to conflicts with changes in the C++ WP the shared_ptr changes were not applied.

I offered to write this paper showing the changes relative to the current WP. I failed to do so before Oulu in time for the CD, but provide it now.

Proposed Wording

Changes are relative to N4594. Clauses have been renumbered since that draft, so cross-references are given using [stable.names] for clarity.

Modify the class synopsis in 20.10.2.2 Class template shared_ptr [util.smartptr.shared]:

  namespace std {
    template<class T> class shared_ptr {
    public:

      typedef T element_type;
      
      [...]
      template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
      template<class Y> shared_ptr(const shared_ptr<Y>& r, T* p) noexcept;
      shared_ptr(const shared_ptr& r) noexcept;
      [...]

      // 20.10.2.2.5, observers:
      T* get() const noexcept;
      T& operator*() const noexcept;
      T* operator->() const noexcept;
      
      long use_count() const noexcept;
      bool unique() const noexcept;
      explicit operator bool() const noexcept;
      template<class U> bool owner_before(shared_ptr<U> const& b) const;
      template<class U> bool owner_before(weak_ptr<U> const& b) const;
    };

    [...]

    // 20.10.2.2.9, 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;
    
      

Add a new paragraph at the end of 20.10.2.2 Class template shared_ptr [util.smartptr.shared]:

Change 20.10.2.2.1 shared_ptr constructors [util.smartptr.shared.const]:

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

-4- _Requires: p shall be convertible to T*. Y shall be a complete type. The expression delete p shall be well formed, shall have well defined behavior, and shall not throw exceptions.

-5- Effects: Constructs a shared_ptr object that owns the pointer p. Enables shared_from_this with p. If an exception is thrown, delete p is called .

-6- Postconditions: use_count() == 1 && get() == p.

-7- Throws: bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.

  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);

-8- Requires: p shall be convertible to T*. D shall be CopyConstructible. The copy constructor and destructor of D shall not throw exceptions. The expression d(p) shall be well formed, shall have well defined behavior, and shall not throw exceptions. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.

-9- Effects: Constructs a shared_ptr object that owns the object p and the deleter d. 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.

-10- Postconditions: use_count() == 1 && get() == p.

-11- Throws: bad_alloc, or an implementation-defined exception when a resource other than memory could not be obtained.

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

-12- Effects: [...]

-13- Postconditions: [...]

-14- [Note: [...]

-15- [Note: [...]

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

-16- Remark: The second constructor shall not participate in overload resolution unless Y* is implicitly convertible to T*.

-17- Effects: [...]

-18- Postconditions: [...]

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

-19- Remark: The second constructor shall not participate in overload resolution unless Y* is convertible to T*.

-20- Effects: [...]

-21- Postconditions: [...]

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

-22- Requires: Y* shall be convertible to T*.

-23- Effects: [...]

-24- Postconditions: [...]

-25- Throws: [...]

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

-26- Remark: This constructor shall not participate in overload resolution unless unique_ptr<Y, D>::pointer is convertible to T*.

-27 Effects: [...]

Change 20.10.2.2.5 shared_ptr observers [util.smartptr.shared.obs]:

  T* get() const noexcept;

-1- Returns: the stored pointer.

  T& operator*() const noexcept;

-2- Requires: get() != 0.

-3- Returns: *get().

-4- Remarks: When T is (possibly cv-qualified) 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;

-5- Requires: get() != 0.

-6- Returns: get().

  

Change 20.10.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;

-1- Requires: The expression static_cast<T*>(r.get()) shall be well formed.

-2- Returns: If r is empty, an empty shared_ptr<T>; otherwise, a shared_ptr<T> object that stores static_cast<T*>(r.get()) and shares ownership with r.

-3- Postconditions: w.get() == static_cast<T*>(r.get()) and w.use_count() == r.use_count(), where w is the return value.

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

-5- Requires: The expression dynamic_cast<T*>(r.get()) shall be well formed and shall have well defined behavior.

-6- Returns:
— When dynamic_cast<T*>(r.get()) returns a nonzero value, a shared_ptr<T> object that stores a copy of it and shares ownership with r ;
— Otherwise, an empty shared_ptr<T> object.

-7- Postcondition: w.get() == dynamic_cast<T*>(r.get()), where w is the return value.

-8- [Note: The seemingly equivalent expression shared_ptr(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;

-9- Requires: The expression const_cast<T*>(r.get()) shall be well formed.

-10- Returns: If r is empty, an empty shared_ptr<T>; otherwise, a shared_ptr<T> object that stores const_cast<T*>(r.get()) and shares ownership with r.

-11- Postconditions: w.get() == const_cast<T*>(r.get()) and w.use_count() == r.use_count(), where w is the return value.

-12- [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]

  

Change 20.10.2.3.1 weak_ptr constructors [util.smartptr.weak.const]:

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

-3- Remark: The second and third constructors shall not participate in overload resolution unless Y* is implicitly convertible to T*.

Change 20.10.2.7 Smart pointer hash support [util.smartptr.hash]:

        template <class T> struct hash<shared_ptr<T>>

-3- The template specialization shall meet the requirements of class template hash (20.14.14). For an object p of type shared_ptr<T>, hash<shared_ptr<T>>()(p) shall evaluate to the same value as hash<T*>()(p.get()).

Acknowledgments

Thanks to Alisdair Meredith and Tim Song for careful review which caught some accidental discrepancies from P0220R1.