C++ Unique ownership without move semantics (auto_ptr)


Example

C++11

NOTE: std::auto_ptr has been deprecated in C++11 and will be removed in C++17. You should only use this if you are forced to use C++03 or earlier and are willing to be careful. It is recommended to move to unique_ptr in combination with std::move to replace std::auto_ptr behavior.

Before we had std::unique_ptr, before we had move semantics, we had std::auto_ptr. std::auto_ptr provides unique ownership but transfers ownership upon copy.

As with all smart pointers, std::auto_ptr automatically cleans up resources (see RAII):

{
    std::auto_ptr<int> p(new int(42));
    std::cout << *p;
} // p is deleted here, no memory leaked

but allows only one owner:

std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px; 
  // px is now empty 

This allows to use std::auto_ptr to keep ownership explicit and unique at the danger of losing ownership unintended:

void f(std::auto_ptr<X> ) {
    // assumes ownership of X
    // deletes it at end of scope
};

std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
       // px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete

The transfer of ownership happened in the "copy" constructor. auto_ptr's copy constructor and copy assignment operator take their operands by non-const reference so that they could be modified. An example implementation might be:

template <typename T>
class auto_ptr {
    T* ptr;
public:
    auto_ptr(auto_ptr& rhs)
    : ptr(rhs.release())
    { }

    auto_ptr& operator=(auto_ptr& rhs) {
        reset(rhs.release());
        return *this;
    }

    T* release() {
        T* tmp = ptr;
        ptr = nullptr;
        return tmp;
    }

    void reset(T* tmp = nullptr) {
        if (ptr != tmp) {
            delete ptr;
            ptr = tmp;
        }
    }

    /* other functions ... */
};

This breaks copy semantics, which require that copying an object leaves you with two equivalent versions of it. For any copyable type, T, I should be able to write:

T a = ...;
T b(a);
assert(b == a);

But for auto_ptr, this is not the case. As a result, it is not safe to put auto_ptrs in containers.