Removing auto_ptr (original) (raw)
ISO/IEC JTC1 SC22 WG21
N4168
Billy Baker
2014-10-02
Introduction
At Oxford in 2007, N1856 was accepted adding unique_ptr and making auto_ptrdeprecated. At CppCon in 2014, Howard Hinnant stated that he would like to see auto_ptr removed from future C++ standards. This paper proposes that auto_ptr be removed entirely. Removing auto_ptrnow would give plenty of time before the next C++ standard for code to be updated if necessary.
Proposal
The conclusion of N1856 for deprecating auto_ptr was:
Conclusion:
One should not move from lvalues using copy syntax. Other syntax for moving should be used instead. Otherwise generic code is likely to initiate a move when a copy was intended.
auto_ptrmoves from lvalues using copy syntax and is thus fundamentally unsafe.
N1856 also pointed out that one feature of auto_ptr was no longer valid.
auto_ptrwas originally intended to allow for easy conversions between base and derived types. However recent changes to the standard result inauto_ptr<Base>not cooperating as originally intended withauto_ptr<Dervied>:struct B {virtual ~B() {}}; struct D : B {};
auto_ptr ap_factory() { return auto_ptr(); }
void ap_sink(auto_ptr);
void test() { auto_ptr ap = ap_factory(); // error: // no suitable copy constructor ap_sink(ap_factory()); // error: no suitable copy constructor }
In the 7 years since the adoption of N1856, the material for learning C++ has made it clear that auto_ptr should not be used. In The C++ Standard Library Second Edition, Nico Josuttis lists problems with auto_ptr.
- At the time of its design, the language had no move semantics for constructors and assignment operators. However, the goal was still to provide the semantics of ownership transfer. As a result, copy and assignment operators got a move semantic, which could cause serious trouble, especially when passing an
auto_ptras argument- There was no semantic of a deleter, so you could use it only to deal with single objects allocated with new.
- Because this was initially the only smart pointer provided by the C++ standard library, it was often misused, especially assuming that it provided the semantics of _shared ownership_as class
shared_ptrdoes now.
Herb Sutter's GotW #89 on Smart Pointers answers the question of "What's the deal with auto_ptr?"
auto_ptris most charitably characterized as a valiant attempt to create aunique_ptrbefore C++ had move semantics.auto_ptris now deprecated, and should not be used in new code.If you have
auto_ptrin an existing code base, when you get a chance try doing a global search-and-replace ofauto_ptrtounique_ptr; the vast majority of uses will work the same, and it might expose (as a compile-time error) or fix (silently) a bug or two you didn’t know you had.
While simple find and replace would work, tools have also been developed to modernize C++ code using transformations such as replacing auto_ptr with unique_ptr.
Proposed wording
These proprosed changes are relative to N3797
Change in 20.7.2 (memory.syn):
// D.10, auto_ptr (deprecated)template class auto_ptr;
Change 20.8.1.2 (unique.ptr.single):
// 20.8.1.2.1, constructors constexpr unique_ptr() noexcept; explicit unique_ptr(pointer p) noexcept; unique_ptr(pointer p, see below d1) noexcept; unique_ptr(pointer p, see below d2) noexcept; unique_ptr(unique_ptr&& u) noexcept; constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { } template <class U, class E> unique_ptr(unique_ptr<U, E>&& u) noexcept;
templateunique_ptr(auto_ptr&& u) noexcept;
Change 20.8.1.2.1 (unique_ptr.single.ctor) to delete paragraphs 22 through 24.
templateunique_ptr(auto_ptr&& u) noexcept;
-22- Effects: Constructs a unique_ptr object, initializing the stored pointer with u.release() and valueinitializingthe stored deleter.-23- Postconditions: get() yields the value u.get() yielded before the construction. u.get() == nullptr.get_deleter() returns a reference to the stored deleter.-24- Remarks: This constructor shall not participate in overload resolution unless U* is implicitly convertibleto T* and D is the same type as default_delete.
Change in 20.8.2.2 (util.smartptr.shared):
// 20.8.2.2.1, constructors: constexpr shared_ptr() noexcept; template 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 shared_ptr(nullptr_t p, D d); template <class D, class A> shared_ptr(nullptr_t p, D d, A a); template shared_ptr(const shared_ptr& r, T* p) noexcept; shared_ptr(const shared_ptr& r) noexcept; template shared_ptr(const shared_ptr& r) noexcept; shared_ptr(shared_ptr&& r) noexcept; template shared_ptr(shared_ptr&& r) noexcept; template explicit shared_ptr(const weak_ptr& r);
template shared_ptr(auto_ptr&& r);template <class Y, class D> shared_ptr(unique_ptr<Y, D>&& r); constexpr shared_ptr(nullptr_t) : shared_ptr() { }// 20.8.2.2.2, destructor: ~shared_ptr();
// 20.8.2.2.3, assignment: shared_ptr& operator=(const shared_ptr& r) noexcept; template shared_ptr& operator=(const shared_ptr& r) noexcept;
shared_ptr& operator=(shared_ptr&& r) noexcept; template shared_ptr& operator=(shared_ptr&& r) noexcept;
template shared_ptr& operator=(auto_ptr&& r);template <class Y, class D> shared_ptr& operator=(unique_ptr<Y, D>&& r);
Change 20.8.2.2.1 (util.smartptr.shared.const) to remove paragraphs 28 through 32:
template shared_ptr(auto_ptr&& r);
-28- Requires: r.release() shall be convertible to T*. Y shall be a complete type. The expression deleter.release() shall be well formed, shall have well defined behavior, and shall not throw exceptions.-29- Effects: Constructs a shared_ptr object that stores and owns r.release().-30- Postconditions: use_count() == 1 && r.get() == 0.-31- Throws: bad_alloc, or an implementation-defined exception when a resource other than memorycould not be obtained.-32- Exception safety: If an exception is thrown, the constructor has no effect.
Change 20.8.2.2.3 (util.smartptr.shared.assign):
shared_ptr& operator=(const shared_ptr& r) noexcept; template shared_ptr& operator=(const shared_ptr& r) noexcept;
template shared_ptr& operator=(auto_ptr&& r);-1- Effects: Equivalent to shared_ptr(r).swap(*this). -2- Returns: *this.
Remove D.10 (depr.auto.ptr).
LWG Issues Impact
2118. [CD] unique_ptr for array does not support cv qualification conversion of actual argument
Issue 2118 has a current status of Review. The original wording to resolve 2118 involved additional usage of auto_ptr. This wording has been superseded by N4089 which does not reference auto_ptr. The proposed wording changes from this proposal should not impact N4089.
2399. shared_ptr's constructor from unique_ptr should be constrained
Issue 2399 has a current status of Ready. Stephan noted that this issue would affect the shared_ptr constructor from auto_ptr. With the proposed changes, the noted issue with the shared_ptr constructor from auto_ptrwould no longer be valid. The current wording to resolve 2399 should not be impacted.
Acknowledgements
Thanks to Howard Hinnant for the inspiration and for reviewing an early draft.