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_ptr moves 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_ptr was originally intended to allow for easy conversions between base and derived types. However recent changes to the standard result in auto_ptr<Base> not cooperating as originally intended with auto_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.

Herb Sutter's GotW #89 on Smart Pointers answers the question of "What's the deal with auto_ptr?"

auto_ptr is most charitably characterized as a valiant attempt to create a unique_ptrbefore C++ had move semantics. auto_ptr is now deprecated, and should not be used in new code.

If you have auto_ptr in an existing code base, when you get a chance try doing a global search-and-replace of auto_ptr to unique_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; template unique_ptr(auto_ptr&& u) noexcept;

Change 20.8.1.2.1 (unique_ptr.single.ctor) to delete paragraphs 22 through 24.

template unique_ptr(auto_ptr&& u) noexcept;

-22- Effects: Constructs a unique_ptr object, initializing the stored pointer with u.release() and valueinitializing the 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 convertible to 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 delete r.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 memory could 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.