std::move vs std::auto_ptr? - c++

What can I do with 'move' (r-value references) in C++11 what I can't with std::auto_ptr? (As I understand they are different implementations of one idea.)
And old question again: is std::auto_ptr so bad component?

C++98/03 has no notion of truly "movable" classes. auto_ptr is a class with transfer-on-copy-semantics, i.e. when you make a copy, the content of the original changes (note the copy constructor with non-const argument!). This is Bad. Such a class cannot be used in the standard containers.
C++11 introduces the concept of truly movable classes thanks the the newly added notion of rvalue references. The new unique_ptr, which replaces auto_ptr entirely, is no longer copyable at all, but instead it is movable. All standard containers have been updated to attempt to move objects if possible, so it is now possible to store move-only objects in standard containers. Other examples of object which are only movable but not copyable are mutexes, locks, threads and iostreams.
To hammer in the point, compare a hypothetical, broken, C++98 piece of code with the corresponding C++11 code:
std::auto_ptr<Foo> p1(new Foo);
std::vector< std::auto_ptr<Foo> > v1;
//v1.push_back(p1); // eeww, what is the state of p1 now? ERROR
std::unique_ptr<Foo> p2(new Foo);
std::vector<std::unique_ptr<Foo>> v2;
//v2.push_back(p2); // Error, copying is simply not allowed
v2.push_back(std::move(p2)); // explicit; mustn't read p2 after moving it
v2.emplace_back(new Foo); // even better ;-)

The problem with std::auto_ptr is that it has copy operations which works like move operations. Therefore algorithms which work with copy operations can work on auto_ptr, but they don't behave as expected, since copied from objects have changed. As such auto_ptr can't be used with STL containers, however code which tries to do so will compile, but fail to work at runtime.
std::unique_ptr on the other hand doesn't have copy operations, but is only moveable instead. Therefore algorithms which copy std::unique_ptr will fail to compile when they should operate on std::unique_ptr. If something uses move operations, it doesn't expect the source of the move operation to remain the same, so no confusion there.
So basically it comes down to the operations working as is expected for a C++ object (or not).

One big difference is rvalue references (and assorted move optimizations) are automatically inferred/deducted from the calling context, whereas you need to create auto_ptr's manually at the call site.

auto_ptr is fundamentally broken, and rvalue references aren't. It's just that simple.

What can I do with 'move' (r-value references) in C++11 what I can't
with std::auto_ptr?
The very most important benefit of move, unique_ptr, etc, over auto_ptr is what you can't do, but can with auto_ptr.
This link explains the rationale to the committee for deprecating auto_ptr. It contains this conclusion:
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.
For details on how this conclusion was reached, read the link.

Related

Why a unique_ptr can be used with std containers, vectors<> for example?

I understand that auto_ptr cannot be used with vectors since auto_ptr does not meet the requirement of being a copy constructible. Since the auto_ptr being copied is modified, copying does not result in two exact copies thereby violating the copy constructible idiom.
Unique_ptr also seem to do the same; it modifies the object being copied - the pointer member of the object being copied is set to nullptr.
Then, how is it possible to use uinque_ptr with vectors and not the auto_ptrs ?
Is my understanding correct or am I missing something here?
auto_ptr <int> autoPtr(new int);
vector < auto_ptr <int> > autoVec;
autoVec.push_back(autoPtr); //compiler error..why?
unique_ptr <int> uniquePtr(new int);
vector < unique_ptr <int> > uniqueVec;
uniqueVec.push_back(std::move(uniquePtr)); //okay..why?
The problem with auto_ptr was that an operation that wasn't expected to modify an object did modify it. The copy constructor modified the source.
unique_ptr doesn't have a copy constructor. It has a move constructor. The move constructor also modifies the source, but this is expected, and it's possible for generic code to distinguish between situations that use the copy constructor and those that use the move constructor.
vector (and the other containers) didn't just magically work with unique_ptr once it was written. They had to be updated so that they could work with move-only types. This was done in C++11. It was possible to make this modification because moving and copying are different operations. It wasn't possible to do the same for auto_ptr because that class had a weird copy constructor.
The compilation error is because of an attempt to modify the auto_ptr const& formal argument (by copying it) in the push_back implementation.
Instead of
autoVec.push_back(autoPtr);
you can do
autoVec.emplace_back( autoPtr.release() );
or more directly (not using the autoPtr variable)
autoVec.emplace_back( new int(42) );
However, while this works in a technical sense (disclaimer: code not even glanced at by any compiler) you still have the problem of auto_ptr items nulling themselves as a result of attempted copying. So this is all very very unsafe. Additionally, auto_ptr was deprecated in C++11; I do not know whether it has been altogether removed now, but that's not unlikely.

auto_ptr vs unique_ptr in containers & algorithms

I understand auto_ptr has screwed up copy semantics and therefore is not safe for use in containers since copying one auto_ptr to another will make the source = NULL pointer (isn't this like move semantics anyway??). But then again, unique_ptr cannot be copied at all and can only transfer ownership. So, how is unique_ptr usable in containers and algorithms that need to use copy operations to copy and re-arrange elements?
There is an in-depth explanation of why auto_ptr is dangerous, while unique_ptr is not:
N1856 : Why deprecate auto_ptr?
The main argument is that in generic code, something that has the syntax of a copy, should be a copy, not a move:
template <class It>
void sort(It first, It last)
{
// ...
value_type pivot_element = *mid_point;
// ...
}
In the above example, the generic code is highly likely to have logic that demands that pivot_element and *mid_point be equivalent after the copy construction shown. This may or may not be generic code in the std::lib. It might be generic code you have written.
When value_type turns out to be a std::auto_ptr<T>, then the above code compiles, but the assumption that pivot_element == *mid_point fails. A run-time error follows.
When value_type turns out to be a std::unique_ptr<T>, then the above code fails at compile-time (because you can't copy a std::unique_ptr<T>). Thus use of std::unique_ptr<T> in preference to std::auto_ptr<T> effectively turns run time errors into compile-time errors.
Now it is also true that within the std::lib, algorithms such as sort have been respecified such that they are not allowed to copy value_type. So it actually is safe to sort a sequence of auto_ptr<T> now (using std::sort). However std::unique_ptr<T> completely replaces the functionality of auto_ptr<T>, and auto_ptr<T> is still dangerous to use in generic code that does use copying (unique_ptr<T> fails to compile when used in such generic code).
So unique_ptr is safer to use than auto_ptr because it refuses to compile when used with generic code that copies.
They don't need to use copy operations anymore in the vast majority of cases. Move-only types like unique_ptr are first-class citizens. That's why move semantics is such a great improvement for both performance and correctness.

C++ Containers and right hand side

I've read today that you should not use the STL containers for auto_ptr because of
the fact that the auto_ptr deletes it rhs value in the = operator.
So i have 2 question :
1) Does this mean that all classes that have this behavior should not be used in containers?
2) what sort of containers can you use?
1) Does this mean that all classes that have this behavior should not
be used in containers?
Yes indeed, because that is not correct copying behaviour, since the copy is not equal to the source afterwards but destroys the source. This is kind of a broken implementation of move-semantics before C++11, required for the strict unique ownership semantics of std::auto_ptr.
2) what sort of containers can you use?
The real answer is actually, that classes having this behaviour (a copy constructor/assignment destroying its source) should just not exist. And fortunately this is not needed anymore nowadays, since C++11 has proper move-semantics, which realize exactly this destructive copy but in a safe way (simply said, only when the source is really not needed anymore).
Therefore std::auto_ptr is deprecated and should not be used anymore. It has been replaced by std::unique_ptr which is movable but not copyable. But since C++11 containers rather move their elements than copy when appropriate, a std::unique_ptr can be used perfectly inside of standard containers. You just cannot copy the container or fill it with a single object which would require copies of std::unique_ptrs, but those operations should not work anyway, since they are conceptually wrong for unique ownership semantics.
And as a side note, if you actually chose std:auto_ptr for a reason, that is you want unique ownership semantics, then a std::shared_ptr (as suggested by other answers) is plain wrong since it exhibits shared ownership. std::unique_ptr is today's std::auto_ptr. Never spam std::shared_ptrs where std::unique_ptrs (or even raw pointers, but from your question I rule that option out) are appropriate.
Auto pointer has a very strict ownership: it and only it is responsible for the lifetime of the object it points at. If you copy an auto_ptr, you lose the reference to what it pointed at.
The problem is in the way STL containers do their stuff. For example, when you are adding an element, the container might expand to get more memory, which leads to copying all the values to the new memory, which, itself, leads to losing the auto_ptrs.
I think that associative containers might not copy themselves entirely when they allocate additional memory, but I'm absolutely not sure, if someone can confirm it, please post a comment, or just edit my answer. Anyway, you'd better not risk it.
Also note that Auto_ptr is deprecated since C++0x, it is advised to use unique_ptr instead. In your case, std::shared_ptr will probably do the trick, unless you really need unique ownership for those objects of yours.
Exactly.
In general, sequence container elements must be CopyConstructible and Assignable. This means that they require:
public copy constructor
public assignment operator
Asociative containers (set<> and map<>) also must provide strict weak ordering, i.e. operator < must be defined (or a dedicated comparison function).
Chapter 23.1 of C++ standard provides detailed requirements.

C++ std::auto_ptr copy constructor

std::auto_ptr lacks const copy constructor, therefore I cannot use it directly in collections. is there some way to have for example vector of std::auto_ptr without using boost pointer collection template?
If you have a C++0x compiler you can use shared_ptr or unique_ptr as appropriate.
There is a good example of correct unique_ptr usage here courtesy of #James McNellis. For a shared_ptr walkthrough look here, courtesy of #D.Shawley. [Upvotes would still be appreciated on those threads, I am sure.]
vector of auto_ptr is always invalid, although Visual C++ v6 disagreed.
No, you just can't have a vector of std::auto_ptr, though there exist many speculations that you can. But if your compiler supports C++0x, you can use std::unique_ptr, which is the new alternative of the deprecated auto pointer which, quote from the new standard, provides a superior alternative. See also this thread
auto_ptr is designed for auto deletion when a variable leaves scope. You don't want to use it in a collection, instead as mentioned above you want to use something like shared_ptr.
Example of auto_ptr's typical use:
void foo()
{
auto_ptr<int> bar = auto_ptr<int>(new int);
...
return; //memory held by auto_ptr is automatically deleted
}
Anything beyond this use is potentially dangerous and/or broken if you are not sure of the special semantics of auto_ptr. (Edit: clarify based on Armen's comment)

std::auto_ptr to std::unique_ptr

With the new standard coming (and parts already available in some compilers), the new type std::unique_ptr is supposed to be a replacement for std::auto_ptr.
Does their usage exactly overlap (so I can do a global find/replace on my code (not that I would do this, but if I did)) or should I be aware of some differences that are not apparent from reading the documentation?
Also if it is a direct replacement, why give it a new name rather than just improve the std::auto_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.
std::auto_ptr and std::unique_ptr are incompatible in someways and a drop in replacement in others. So, no find/replace isn't good enough. However, after a find/replace working through the compile errors should fix everything except weird corner cases. Most of the compile errors will require adding a std::move.
Function scope variable:
100% compatible, as long as you don't pass it by value to another function.
Return type:
not 100% compatible but 99% compatible doesn't seem wrong.
Function parameter by value:
100% compatible with one caveat, unique_ptrs must be passed through a std::move call. This one is simple as the compiler will complain if you don't get it right.
Function parameter by reference:
100% compatible.
Class member variable:
This one is tricky. std::auto_ptrs copy semantics are evil. If the class disallows copying then std::unique_ptr is a drop in replacement. However, if you tried to give the class reasonable copy semantics, you'll need to change the std::auto_ptr handling code. This is simple as the compiler will complain if you don't get it right. If you allowed copying of a class with a std::auto_ptr member without any special code, then shame on you and good luck.
In summary, std::unique_ptr is an unbroken std::auto_ptr. It disallows at compile time behaviors that were often errors when using a std::auto_ptr. So if you used std::auto_ptr with the care it needed, switching to std::unique_ptr should be simple. If you relied on std::auto_ptr's odd behavior, then you need to refactor your code anyway.
AFAIK, unique_ptr is not a direct replacement. The major flaw that it fixes is the implicit transfer of ownership.
std::auto_ptr<int> a(new int(10)), b;
b = a; //implicitly transfers ownership
std::unique_ptr<int> a(new int(10)), b;
b = std::move(a); //ownership must be transferred explicitly
On the other hand, unique_ptr will have completely new capabilities: they can be stored in containers.
Herb Sutter has a nice explanation on GotW #89:
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.
In other words, while a global search-and-replace may "break" your code temporarily, you should do it anyway: It may take some time to fix the compile errors, but will save you a lot more trouble in the long run.