Std::Auto_Ptr to Std::Unique_Ptr

std::auto_ptr to std::unique_ptr

You cannot do a global find/replace because you can copy an auto_ptr (with known consequences), but a unique_ptr can only be moved. Anything that looks like

std::auto_ptr<int> p(new int);
std::auto_ptr<int> p2 = p;

will have to become at least like this

std::unique_ptr<int> p(new int);
std::unique_ptr<int> p2 = std::move(p);

As for other differences, unique_ptr can handle arrays correctly (it will call delete[], while auto_ptr will attempt to call delete.

C++ std:.auto_ptr or std::unique_ptr (to support multiple compilers, even old C++03 compilers)?

As you noted, std::auto_ptr<> has been deprecated in C++11
(Reference).

Moving to c++11 std::unique_ptr<> is the right way, as also stated by Herb Sutter in
GotW89:


  1. What’s the deal with auto_ptr?

    auto_ptr is most charitably characterized as a valiant attempt to create a unique_ptr before 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.

Please also note that C++17 is going to remove std::auto_ptr.

I think there may be different ways of solving your problem, the "right" one also depends on how your actual code is written.

A few options are:

Option 1

Use boost::unique_ptr

Option 2

Conditionally use auto_ptr or unique_ptr based on __cplusplus.

class Myclass {

#if __cplusplus < 201103L

std::auto_ptr m_ptr;

#else

std::unique_ptr m_ptr;

#endif

...

This will be scattered in every place where you reference auto_ptr, I don't really like it.

May be look less awkward if all your references to std::auto_ptr are already typedef'ed (just conditionally change the typedef).

Option 3

Conditionally use using and aliasing to "define" auto_ptr (and reference it without std:: namespace).

#if __cplusplus < 201103L

using std::auto_ptr;

#else

template

using auto_ptr = std::unique_ptr;

#endif

Drawback: you keep using "auto_ptr", but in c++11 it means std::unique_ptr.

Really confusing...

Option 3.1

Probably slightly better than option 2:

reverse using aliases and prefer unique_ptr name.

Option 4

Wrap the std:: smart pointer (conditionally auto_ptr or unique_ptr) in your own defined template smart pointer class.

This may be cumbersome and requires search and replacement of all auto_ptr references with your new class.

Other dirty options

Other options involve definitions inside the std:: namespace, which I think is prohibited by the standard,

or using preprocessor #define to ...ehm... "rename" unique_ptr to auto_ptr just for old C++03 compilers.

macro for switching between auto_ptr and unique_ptr

For just nullptr and unique_ptr, this could work:

#ifndef HAVE_CXX11
#define nullptr NULL
#define unique_ptr auto_ptr
#endif

But I don't know how you plan to cope with the different semantics of unique_ptr and auto_ptr.

If you're willing to live with some undefined behaviour for a while (of the kind unlikely to cause actual issues), you could also provide your own std::move:

namespace std {

template <class T>
T& move(T &x) { return x; }

}

It's UB, because you're not allowed to add anything to namespace std. But if it's only a temporary measure, it should be safe (the pre-11 compiler is unlikely to have the name std::move).

unique_ptr VS auto_ptr

std::auto_ptr<T> may silently steal the resource. This can be confusing and it was tried to defined std::auto_ptr<T> to not let you do this. With std::unique_ptr<T> ownership won't be silently transferred from anything you still hold. It transfers ownership only from objects you don't have a handle to (temporaries) or which are about to go away (object about to go out of scope in a function). If you really want to transfer ownership, you'd use std::move():

std::unique_ptr<bar> b0(new bar());
std::unique_ptr<bar> b1(std::move(b0));

Why unique_ptr works but auto_ptr doesn’t with STL

You're looking at the whole thing backwards.

In C++98/03, we got auto_ptr. This type lies to everyone by pretending that it supports copy semantics when in fact copying it does something very much unlike a copy operation. Therefore, any type which relies on a type providing copy semantics, like certain containers, would not take well to getting an auto_ptr. Of course, you will only find out when your code becomes dysfunctional, not at compile time.

In C++11, we got unique_ptr, a type which explicitly does not provide copy semantics. Instead, it provides move semantics, and provides them correctly. Therefore, any type which relies on a type providing copy semantics will fail to compile when given a unique_ptr.

However, the reason unique_ptr came into being at all because the concept of moving an object was added to the language in C++11. When new concepts get added to the language, existing tools, like standard library requirements, are often re-evaluated relative to that language feature.

For example, types that formerly required copy semantics did not necessarily have to keep that requirement. C++98/03 containers that required copy semantics were updated in C++11 to only require (noexcept) move semantics from a type.

So it's not that unique_ptr fulfills some requirement that auto_ptr did not. It's that the language changed to no longer need that requirement, but auto_ptr was still lying about what it did, so for backwards compatibility sake, we created a new type that respected the new language features and didn't lie to people.

std::unique_ptr conversion constructor from std::auto_ptr

Seems to be a bug.

I get the same error as you in VC 2010 but it works fine with VC 2012.

I'm not too good at tracking standard changes, but it seems this functionality was only added to the standard with N3073 (see change 10. on that page) which postdates the release of VC 2010.

Advantages of unique_ptr over auto_ptr?

unique_ptr forces you to be explicit about transferring ownership, so it is visible and clear in the code. With auto_ptr it is too easy to get silent transfers of ownership and when reading the code it's not always clear whether ownership is transferred, and if it is, whether it was intended by the author of the code or is a bug! When you see a unique_ptr used with std::move it is obvious that the intention is to transfer ownership.

unique_ptr properly supports move semantics, so it only allows ownership transfer from temporaries and moved objects (i.e. rvalues). Containers can detect if a type is "move aware" and act appropriately. auto_ptr does not know about move semantics, and will transfer ownership from lvalues or rvalues, so containers think it's a normal copyable object, but it doesn't behave like one because it modifies its source when copying.

auto_ptr was useful when used carefully, but it was too easy to misuse it and write dangerous code. It is time for it to die. unique_ptr supports everything auto_ptr can do, but is safe by default (you have to try harder to use it wrong) and also has extra features like custom deleters and array support.

using (or other mechanism) to swap in unique_ptr for auto_ptr in C++11?

Understanding that the code is under your control and can be recompiled with or with out C++11 support, an alias can be created for the smart pointer required (either std::unique_ptr or std::auto_ptr).

template <typename T>
struct local_ptr {
#if defined(MYLIB_CXX11)
typedef std::unique_ptr<T> ptr;
#else
typedef std::auto_ptr<T> ptr;
#endif
};

Then used in the client code as such;

local_ptr< PK_MessageAccumulator>::ptr managed = //...

The syntax is more awkward than desired, but this is to accommodate the requirement to support C++03.

In all cases, the long term solution is to factor out the use of auto_ptr or silence the deprecated warnings.



Related Topics



Leave a reply



Submit