Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > auto_ptr

Reply
Thread Tools

auto_ptr

 
 
Joe Laughlin
Guest
Posts: n/a
 
      06-03-2004
Can someone explain why auto_ptr can't be used with STL containers? I've
looked at various explanations, but I couldn't really understand any of
them.

Thanks,
Joe


 
Reply With Quote
 
 
 
 
Leor Zolman
Guest
Posts: n/a
 
      06-03-2004
On Thu, 3 Jun 2004 19:59:05 GMT, "Joe Laughlin"
<> wrote:

>Can someone explain why auto_ptr can't be used with STL containers? I've
>looked at various explanations, but I couldn't really understand any of
>them.


There are certain requirements that STL containers make of the type of the
elements to be contained. Among them are the requirements that elements be
"assignable" and "copy constructible", in the conventional meaning of the
terms. auto_ptr objects break the mold, because when you "copy" one you're
actually re-assigning ownership of the pointed-to object. All hell breaks
loose if you were to place auto_ptrs into STL containers, which is why the
libraries strive to make any attempts to do so draw compilation errors.

If you need a smart pointer to put into an STL container, use one from
Boost.
-leor

>
>Thanks,
>Joe
>


--
Leor Zolman --- BD Software --- www.bdsoft.com
On-Site Training in C/C++, Java, Perl and Unix
C++ users: download BD Software's free STL Error Message Decryptor at:
www.bdsoft.com/tools/stlfilt.html
 
Reply With Quote
 
 
 
 
Joe Laughlin
Guest
Posts: n/a
 
      06-03-2004
Leor Zolman wrote:
> On Thu, 3 Jun 2004 19:59:05 GMT, "Joe Laughlin"
> <> wrote:
>
>> Can someone explain why auto_ptr can't be used with STL
>> containers? I've looked at various explanations, but I
>> couldn't really understand any of them.

>
> There are certain requirements that STL containers make
> of the type of the elements to be contained. Among them
> are the requirements that elements be "assignable" and
> "copy constructible", in the conventional meaning of the
> terms. auto_ptr objects break the mold, because when you
> "copy" one you're actually re-assigning ownership of the
> pointed-to object. All hell breaks loose if you were to
> place auto_ptrs into STL containers, which is why the
> libraries strive to make any attempts to do so draw
> compilation errors.
>
> If you need a smart pointer to put into an STL container,
> use one from Boost.
> -leor


What do you mean by "re-assigning ownership of the pointed-to object"? Do
you mean that a new object is not being created?


 
Reply With Quote
 
Leor Zolman
Guest
Posts: n/a
 
      06-03-2004
On Thu, 3 Jun 2004 21:27:28 GMT, "Joe Laughlin"
<> wrote:

>Leor Zolman wrote:
>> On Thu, 3 Jun 2004 19:59:05 GMT, "Joe Laughlin"
>> <> wrote:
>>
>>> Can someone explain why auto_ptr can't be used with STL
>>> containers? I've looked at various explanations, but I
>>> couldn't really understand any of them.

>>
>> There are certain requirements that STL containers make
>> of the type of the elements to be contained. Among them
>> are the requirements that elements be "assignable" and
>> "copy constructible", in the conventional meaning of the
>> terms. auto_ptr objects break the mold, because when you
>> "copy" one you're actually re-assigning ownership of the
>> pointed-to object. All hell breaks loose if you were to
>> place auto_ptrs into STL containers, which is why the
>> libraries strive to make any attempts to do so draw
>> compilation errors.
>>
>> If you need a smart pointer to put into an STL container,
>> use one from Boost.
>> -leor

>
>What do you mean by "re-assigning ownership of the pointed-to object"? Do
>you mean that a new object is not being created?


Correct - auto_ptr's are designed for a specific purpose: to guard against
resource (memory) leaks in the face of exceptions. Example:

#include <iostream>
#include <memory>
using std::cout;
using std::endl;

class T
{
public:
T() { cout << "T()" << endl; }
~T() {cout << "~T()" << endl; }
};

void bad()
{
T *t = new T;
cout << "In bad()" << endl;
throw "oops in bad -- no destructor";
}

void good()
{
std::auto_ptr<T> p(new T);
std::auto_ptr<T> p2(p); // take one down
std::auto_ptr<T> p3; // pass it around
p3 = p2; // still one T on the wall

cout << "In good()" << endl;
throw "oops in good - should destruct";
}

int main()
{
try {
bad();
} catch (...)
{
cout << "Caught" << endl;
}

cout << endl;

try {
good();
} catch (...)
{
cout << "Caught" << endl;
}


return 0;
}


Output:
T()
In bad()
Caught

T()
In good()
~T()
Caught

Note that T's destructor never runs before returning from bad(), but it
does upon return from good(). And no matter how many time we copy-construct
or assign that auto_ptr, no objects beyond the first get constructed.
-leor

>


--
Leor Zolman --- BD Software --- www.bdsoft.com
On-Site Training in C/C++, Java, Perl and Unix
C++ users: download BD Software's free STL Error Message Decryptor at:
www.bdsoft.com/tools/stlfilt.html
 
Reply With Quote
 
Howard Hinnant
Guest
Posts: n/a
 
      06-04-2004
In article <>,
"Joe Laughlin" <> wrote:

> Can someone explain why auto_ptr can't be used with STL containers? I've
> looked at various explanations, but I couldn't really understand any of
> them.


In addition to Leor's good comments I thought that I would add that any
sequence of auto_ptrs is dangerous if you get it too close to generic
code operating on sequences, even built-in arrays of auto_ptrs.
Consider this reasonable generic algorithm:

#include <iterator>
#include <algorithm>

template <class It, class Comp>
It
my_partition3(It first, It last, Comp comp)
{
typedef typename std::iterator_traits<It>::difference_type
difference_type;
typedef typename std::iterator_traits<It>::value_type
value_type;
difference_type len = std::distance(first, last);
switch (len)
{
case 0:
case 1:
return first;
case 2:
--last;
if (comp(*last, *first))
std::iter_swap(first, last);
return last;
}
It middle = first;
std::advance(middle, len/2);
--last;
if (comp(*middle, *first))
std::iter_swap(first, middle);
if (comp(*last, *first))
{
std::iter_swap(first, last);
std::iter_swap(middle, last);
}
else if (comp(*last, *middle))
std::iter_swap(middle, last);
++last;
value_type partition = *middle;
while (true)
{
while (true)
{
if (first == last)
return first;
if (!comp(*first, partition))
break;
++first;
}
--last;
while (true)
{
if (first == last)
return first;
if (comp(*last, partition))
break;
--last;
}
std::iter_swap(first, last);
++first;
}
}

This algorithm takes a sequence, looks at the first, middle and last
values, and then partitions the sequence based on the median of the
first, middle and last elements. Such an operation is useful in
sorting. Here's an example use:

#include <iostream>
#include <algorithm>
#include <functional>

int main()
{
int ia[9] = {0, 2, 3, 4, 5, 6, 7, 8};
unsigned size = sizeof(ia)/sizeof(ia[0]);
std::random_shuffle(ia, ia + size);
for (int* p = ia; p < ia + size; ++p)
std::cout << *p << ' ';
std::cout << '\n';
int* part = my_partition3(ia, ia+size, std::less<int>());
for (int* p = ia; p < part; ++p)
std::cout << *p << ' ';
std::cout << "- ";
for (int* p = part; p < ia+size; ++p)
std::cout << *p << ' ';
std::cout << '\n';
}

Sample output:

4 2 0 6 8 5 1 7 3
3 2 0 1 - 4 5 6 7 8

The algorithm looked at 4, 8, 3, chose the median 4, and partitioned the
range into those numbers less than 4, and those numbers greater than or
equal to 4.

You can use the same generic algorithm with a smart pointer (say
std::tr1::shared_ptr<int>) by making use of a proper comparator.

#include <iostream>
#include <algorithm>
#include <memory>

struct indirect_less
{
template <class T>
bool operator()(const T& x, const T& y) const
{ return *x < *y; }
};

int main()
{
typedef std::tr1::shared_ptr<int> Ptr;

Ptr ia[9];
Ptr* begin = ia;
unsigned size = sizeof(ia)/sizeof(ia[0]);
Ptr* end = ia + size;
for (int k = 0; k < size; ++k)
ia[k].reset(new int(k));
std::random_shuffle(begin, end);
for (Ptr* k = begin; k < end; ++k)
std::cout << **k << ' ';
std::cout << '\n';
Ptr* i = my_partition3(begin, end, indirect_less());
for (Ptr* k = begin; k < i; ++k)
std::cout << **k << ' ';
std::cout << "- ";
for (Ptr* k = i; k < end; ++k)
std::cout << **k << ' ';
}

4 2 0 6 8 5 1 7 3
3 2 0 1 - 4 5 6 7 8

However, you do not want to use auto_ptr with such a generic algorithm,
even though it compiles!

#include <iostream>
#include <algorithm>
#include <memory>

struct indirect_less
{
template <class T>
bool operator()(const T& x, const T& y) const
{ return *x < *y; }
};

int main()
{
typedef std::auto_ptr<int> Ptr;

Ptr ia[9];
Ptr* begin = ia;
unsigned size = sizeof(ia)/sizeof(ia[0]);
Ptr* end = ia + size;
for (int k = 0; k < size; ++k)
ia[k].reset(new int(k));
std::random_shuffle(begin, end);
for (Ptr* k = begin; k < end; ++k)
std::cout << **k << ' ';
std::cout << '\n';
Ptr* i = my_partition3(begin, end, indirect_less());
for (Ptr* k = begin; k < i; ++k)
std::cout << **k << ' ';
std::cout << "- ";
for (Ptr* k = i; k < end; ++k)
std::cout << **k << ' ';
}

The result is a crashing program. The culprit is this very reasonable
line in the generic algorithm:

value_type partition = *middle;

With most types a /copy/ of *middle* is put into partition (leaving
*middle unchanged). With auto_ptr, *middle is /moved/ into partition.
This fundamentally alters the logic of the algorithm in a way not
anticipated. If you're lucky, you'll get an immediate crash. If you're
unlucky, you'll get a subtle run time error that doesn't show up until
after shipping.

This is the main reason that auto_ptr was redesigned late in the game
for C++98 so that you could not create std::container<auto_ptr>. But
built-in arrays are still just as dangerous and compile just fine. The
problem is not with std::containers, but with auto_ptr:

Howard's rule about move: It is dangerous to design a class such that
it moves from lvalues with copy syntax. But it is safe to move from
rvalues (temporaries) with copy syntax. To safely move from lvalues,
one should use syntax other than copy.

auto_ptr is dangerous. Use it with care. Keep it away from generic
algorithms as you would gasoline away from flames. I hope to help put a
better alternative into C++0X:

http://www.open-std.org/jtc1/sc22/wg...77.htm#move_pt
r%20Example

-Howard
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
explicit auto_ptr<T>::auto_ptr(T*) ? Sousuke C++ 9 03-16-2010 11:54 AM
auto_ptr<Derived> to auto_ptr<Base> Siemel Naran C++ 2 01-11-2005 04:45 AM
auto_ptr to char[ size_t]? Marc Schellens C++ 8 07-17-2003 09:11 AM
auto_ptr alg C++ 4 07-13-2003 11:27 PM
shared_ptr vs std::auto_ptr SerGioGio C++ 3 07-03-2003 02:52 PM



Advertisments