Reputation: 99
What would be a simple C++ program where a std::vector<std::auto_ptr<T>>
compiles but fails to execute correctly, whereas the same program with std::vector<std::unique_ptr<T>>
compiles and works correctly, for some data type T
?
I know that std::auto_ptr
has been deprecated or removed; I just want an example involving containers to motivate why it was deprecated or removed.
I'm using g++-10 -std=c++20
on MacOS Big Sur version 11.2.1.
Upvotes: 2
Views: 112
Reputation: 123114
The problem with auto_ptr
is that it can be copied and making a copy modifies the original:
a = b; // transfers ownership from b to a
This is similar to what moving does now, just that at the time of auto_ptr
there was no move semantics in the language. Now that C++ has move semantics transfer of ownership can be expressed more clearly:
a = std::move(b);
Nobody would/should expect that b
is not modified in that line.
However, with a = b
it is commonly assumed, that b
is not modified. auto_ptr
breaks that assumption. Consider for example:
template <typename P>
void foo() {
std::vector<P> x;
x.resize(42);
int i=0;
for (auto& e : x) e.reset(new int(i++));
for (auto e : x) {
std::cout << *e << "\n";
}
for (auto e : x) {
std::cout << *e << "\n";
}
}
With P=std::unique_ptr<int>
this will cause a compiler error:
<source>:17:15: error: call to deleted constructor of 'std::unique_ptr<int, std::default_delete<int> >'
for (auto e : x) {
^ ~
While it compiles with P=std::auto_ptr<int>
but is undefined behavior (eg segfault here: https://godbolt.org/z/93hdse), because it dereferences null pointers.
Similar issue with any algorithm assumes it is "ok" to copy elements. For example a comparator for auto_ptr
that takes parameters by value does compile but causes havoc:
auto compare = [](auto a,auto b) { return *a < *b; }
std::sort(x.begin(), x.end(),compare); // BOOM !
Not always is it so obvious that a copy is being made, algorithms may copy elements internally when elements are copyable.
Upvotes: 0
Reputation: 597941
std::auto_ptr
simply cannot be used in standard containers at all. It does not maintain proper semantics under that situation. Which is one of the reasons why move semantics and std::unique_ptr
were invented in C++11 in the first place. std::auto_ptr
was deprecated in C++11, and removed completely in C++17. So don't use it at all in modern coding.
The official reason why std::auto_ptr
was deprecated is detailed here:
The example given uses std::sort()
on a std::vector<std::auto_ptr<int>>
:
With such a design, one could put auto_ptr into a container:
vector<auto_ptr<int> > vec;
However field experience with this design revealed subtle problems. Namely:
sort(vec.begin(), vec.end(), indirect_less());
Depending upon the implementation of
sort
, the above line of reasonable looking code may or may not execute as expected, and may even crash! The problem is that some implementations ofsort
will pick an element out of the sequence, and store a local copy of it.... value_type pivot_element = *mid_point; ...
The algorithm assumed that after this construction that
pivot_element
and*mid_point
were equivalent. However whenvalue_type
turned out to be anauto_ptr
, this assumption failed, and subsequently so did the algorithm.The fix to this problem was to make
auto_ptr
inhospitable to containers by disallowing "copying" from aconst auto_ptr
. With such anauto_ptr
, one gets a compile time error if you try to put it in a container.
The conclusion at the end comes down to this:
Calling any generic code, whether
std
or not, that will operate onauto_ptr
is risky because the generic code may assume that something that looks like a copy operation, actually is a copy operation.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.
Upvotes: 6