Topic: auto_ptr: :reset and auto_ptr::operator=


Author: bgibbons@taligent.com (Bill Gibbons)
Date: 1995/10/02
Raw View
In article <9527400.19710@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU
(Fergus Henderson) wrote:

> I don't think so.  Instead of returning an auto_ptr<T>, you can
> just return the T* obtained from a call to auto_ptr<T>::release().
> The caller can use this T* to initialize another auto_ptr<T>.

This is very risky.  The whole point of auto_ptr is that it makes code
which handles allocated objects behave well when exceptions propagate
through the scope using the pointer.  To make this work well there are
two very strict requirements:

   * The raw pointer must *always* be owned by some auto_ptr.

   * The raw pointer must *never* be owned by more than one auto_ptr.

Any time you discard the auto_ptr and pass around the raw pointer you
violate the first requirement.  Any time you initialize an auto_ptr with
a raw pointer which belongs to another auto_ptr you violate the second
requirement.

For example:

    struct X { ~X() { throw "oops!"; } };

    T * unsafe_function() {
        auto_ptr<T> p = new T;
        X x;
        return p.release();
    }

Sequence of events:

  (1) The pointer is released, making the function vulnerable.
  (2) "x" is destroyed, causing an exception to be thrown.
  (3) "p" is destroyed, but "p" no longer holds the pointer.
  (4) "unsafe_function" exits with a memory leak.

The interesting cases for assignment and initialization are not uses of
local variables in the same scope, but rather passing arguments to
functions and returning values from functions.  In these contexts it is
necessary to either violate the above requirements (in which case the
program isn't exception-safe) or deal with transfer of ownership directly
in the assignment operator and copy constructor.

Taligent has had years of experience using a variety of templates which
are variants of auto_ptr.  The consensus here is that auto_ptr must have
ownership semantics, and they must be enforced by the assignment operator
and copy constructor.

--
Bill Gibbons
bgibbons@taligent.com

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: lars.farm@nts.mh.se (Lars Farm)
Date: 1995/10/02
Raw View
In article <9527400.19710@mulga.cs.mu.OZ.AU>,
fjh@munta.cs.mu.OZ.AU (Fergus Henderson) wrote:

>I think the assignment operator and copy-constructor of auto_ptr
>ought to be declared private.  If you want to copy an auto_ptr,
>then instead of writing
>
> auto_ptr<T> y = x; // copy constructor
> y = x;   // assignment operator
>
>
>you should have to write
>
> auto_ptr<T> y = x.release();
> y = x.release();
>
>This makes it clear that x is being modified.

Nowhere else in a c++ program will a valid ptr_a become invalid by:

  ptr_b = ptr_a;

Nowhere else in a c++ (or any other) program will the rhs be altered by
assignment.

If I really want to transfer ownership I can do
ptr_b.reset(ptr_a.release()); If I'm unaware of the danger, the compiler
will point me in the right direction. As it stands it will be quite hard to
explain to students why this must behave so counterintuitively. IMHO the
simpler version with private copy/op= will also be easier to use.

Also:

void f( auto_ptr<X> ); // this might not make sense (to you),
                       // but you must be aware of the dangers
                       // with auto_ptr to know... many wont be
void g()
{
   auto_ptr<X> x( new X );
   ...
   x->m     // x is valid
   f( x );
   x->m;    // oops... compiler would catch this with private copy-ctor

I think they should be private.

--
Lars Farm, lars.farm@nts.mh.se

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1995/10/03
Raw View
Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
|> kanze@gabi-soft.fr (J. Kanze) writes:

|> >Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
|> >|> Michael Cook <mcook@cognex.com> writes:
|> >
|> >|> >    auto_ptr<V> byValue(auto_ptr<V> a);
|> >|> >    a = byValue(b);
|> >|> >    //   warning: initialization of non-const `auto_ptr<V> &' from
|> >|> >    //   rvalue `auto_ptr<V>'.
|> >
|> >|> That warning is not bogus; on the contrary, the code is ill-formed,
|> >|> and the draft standard *requires* a diagnostic.
|> >|> (5.2.2/3 says that argument passing is treated as initialization,
|> >|> and 8.5.3/8 says you can't initialize a non-const reference with
|> >|> an rvalue.)
|> >
|> >To which Michael Cook (mcook@cognex.com) responded:
|> >
|> >|> Hmm.  Interesting.  That means the auto_ptr class can't be used that way.
|> >|> That surprises me.
|> >
|> >It not only surprises me, it bothers me profoundly. I find the
|> >parameter declaration of auto_ptr<T>::operator= particularly
|> >problematic.

|> Yes, I agree: I too find the auto_ptr assignment operator very disturbing,
|> though possibly for slightly different reasons.  I don't like the semantics
|> of the assignment operator: it does not just copy the auto_ptr, it
|> also invalidates the source of the copy.  I think that using `operator ='
|> for this operation is an abuse of operator overloading.

After reading your posting, I agree.  This is really the cause of the
problem.  Operator= (and copy construction) has generally accepted
semantics.  The operator= of auto_ptr does not follow these semantics.

|> >On one hand, the right hand side of assignment definitly
|> >*is* modified; declaring it const (and casting away const in operator=
|> >is order to implement the current semantics) would be outright lying to
|> >the user.  It is also lying to the compiler; presumably at some future
|> >date, a compiler will take advantage of this const declaration in order
|> >to implement some fancy optimization, which will break in the case of
|> >casting away const.

|> Right.  This would awful; certainly much worse than the current situation.

|> >On the other hand, using an auto_ptr as the return value of a function
|> >would seem to be one of its principal uses.  Not allowing this would
|> >almost render auto_ptr useless.

|> I don't think so.  Instead of returning an auto_ptr<T>, you can
|> just return the T* obtained from a call to auto_ptr<T>::release().
|> The caller can use this T* to initialize another auto_ptr<T>.

This is awful.  Returning an auto_ptr guarantees that the memory is on
the heap.  A T* contains no such guarantee, and a T* returned from a
function should not be used to initialize an auto_ptr. <grin>

Seriously, there is a part of truth in what I say above.  I detest the
idiom in which functions return pointers to the heap, and expect the
caller to delete them.  I was hoping that auto_ptr would solve this
problem in an elegant way.

Oh well, back to my old RefCntPtr<T> class.

|> >My current solution (in my personal implementation) is to declare the
|> >parameter of operator= const, with *big* warnings in comments at the
|> >point of declaration that this is a lie, and to declare the pointer
|> >member mutable, so as to tell the compiler that the const may be a lie.
|> >Hardly what I would call ideal, but the best I've been able to come up
|> >with til yet.

|> This is an awful abuse of both `mutable' and `const', and a horrible
|> hack, IMHO.  I wouldn't recommend it.

I only recommend it as the lessor of two evils, but you've almost
convinced me to eshew auto_ptr completely.  Which is a shame, because I
very much like what the class is trying to do.

|> >Does anyone have any other solutions, or suggestions as to what the
|> >standard *should* say?

|> I think the assignment operator and copy-constructor of auto_ptr
|> ought to be declared private.  If you want to copy an auto_ptr,
|> then instead of writing

|>  auto_ptr<T> y = x; // copy constructor
|>  y = x;   // assignment operator


|> you should have to write

|>  auto_ptr<T> y = x.release();
|>  y = x.release();

|> This makes it clear that x is being modified.

I agree with this for general use.  The problem is function return
values.  OK, the object in question is being modified, although you
promissed (with const) not to.  But since the object in question ceases
to exist immediatly after the modification, who cares? :-)

Ideally, perhaps, I would want to have assignment and copy construction
defined only when the source is a temporary.  In practice, in my own
code, I find that in fact, all (or almost all) of the assignments and
copies are in fact from temporaries.  (Off hand, I cannot remember any
that wasn't either a function return value, or a parameter to a
function.)  So I find that my solution, while ugly WRT C++ programming,
does correspond to what I expect an auto_ptr to do.
--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle--
                             --Beratung in industrieller Datenverarbeitung
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: kuehl@uzwil.rz.uni-konstanz.de (Dietmar Kuehl)
Date: 1995/10/04
Raw View
J. Kanze (kanze@gabi-soft.fr) wrote:
: Ideally, perhaps, I would want to have assignment and copy construction
: defined only when the source is a temporary.  In practice, in my own
: code, I find that in fact, all (or almost all) of the assignments and
: copies are in fact from temporaries.  (Off hand, I cannot remember any
: that wasn't either a function return value, or a parameter to a
: function.)  So I find that my solution, while ugly WRT C++ programming,
: does correspond to what I expect an auto_ptr to do.

The statement with the temporary gave me the following idea: Using two
classes instead of just one class, it is possible to protect the copy
constructor and the assignment operator while still allowing a function
to return a "safe" object.

The two class are auto_ptr and auto_ptr_tmp. They have the following
properties:

- auto_ptr_tmp has a public destructor
- all other functions of auto_ptr_tmp are private
- auto_ptr is a friend of auto_ptr_tmp
- auto_ptr has a private assignment operator and copy constructor
- auto_ptr has a constructor and assignment operator taking
  a auto_ptr_tmp
- auto_ptr has a conversion operator to auto_ptr_tmp
- everything else is like auto_ptr in the DWP

Using this setup would yield the following:

  auto_ptr_tmp<foo> f1()
  {
    auto_ptr<foo> ap(new foo());
    return ap;   // conversion to auto_ptr_tmp
  }

  void f2(auto_ptr<foo>); // illegal: copy constructor is private
  auto_ptr<foo> f3();  // legal, but (almost) useless

  h()
  {
    auto_ptr<foo> a1;
    auto_ptr<foo> a2(new foo);
    auto_ptr<foo> a3(a2); // illegal: copy constructor is private

    a1 = a2;   // illegal: assignment operator is private

    (auto_ptr_tmp<foo>)a1; // legal: "steal" the pointer of a1
    a1 = (auto_ptr_tmp<foo>)a2; // legal: conversion to auto_ptr_tmp and
    // assignment using auto_ptr_tmp :-(
    a1 = (auto_ptr_tmp<foo>)f3; // legal: conversion to auto_ptr_tmp and
    // assignment using auto_ptr_tmp :-(

    f();   // ~auto_ptr_tmp() takes care of pointer
    a1 = f();   // legal: assignment taking auto_ptr_tmp
    auto_ptr<foo> a4(f()); // legal: constructor taking auto_ptr_tmp
  }

However, some "ugly" parts are present:
- functions can only return a auto_ptr_tmp (without casting)
- it is still possible to assign auto_ptrs using explicit casts
- there is still some const/mutable "hackery" to be able to
  modify a returned auto_ptr_tmp
- using a cast it is possible to "steal" a pointer which is
  unexpected

Although there are ugly parts these two classes prohibit all straight
forward uses which are allowed with auto_ptr and which lead to
unexpected behavior.

dk
--
http://www.informatik.uni-konstanz.de/~kuehl
dietmar.kuehl@uni-konstanz.de
I am a realistic optimist - that's why I appear to be slightly pessimistic


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: eddy@clipper.robadome.com (eddy Gorsuch)
Date: 1995/10/04
Raw View
In article <0099753967FDC3C0.25408CC3@ittpub.nl>,  <wil@ittpub.nl> wrote:
[...]
>But we need the public copy constructor and assignment operator so we can
>define a function that returns an auto_ptr pointer to a newly allocated object,
>like the familar clone() member function found in many polymorphic class
>hierarchies. How to return a pointer to a newly allocated object is a problem
>I've been struggling with for years. All the alternatives I tried were
>unsatisfactory:

Even with private copy constructor and assignment operator, couldn't you use
a function that returns an auto_ptr the exact same way you would use any
other auto_ptr? i.e.
  auto_ptr<T> func();
  auto_ptr<T> myPtr = func().release();
This transfers ownership to the calling function (just as the current
assignment operation does). Actually, I'd prefer an explicit transfer
function:
  myPtr.usurp(func());
which operates like the current definition of the assignment operator.

eddy

--
ed.dy \'ed-e-\ n [ME (Sc dial.) ydy, prob. fr. ON itha; akin to OHG ith-
   again], L et and 1a: a current of water or air running contrary to the main
   current; esp)X : a small whirlpool 1b: a substance moving similarly  2: a
   contrary or circular current  - eddy vb

---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1995/10/04
Raw View
Dag Bruck (dag@net.dynasim.se) wrote:
|> Bill Gibbons <bgibbons@taligent.com> wrote:
|> >
|> >   * The raw pointer must *always* be owned by some auto_ptr.
|> >
|> >   * The raw pointer must *never* be owned by more than one auto_ptr.
|> >.....
|> >Taligent has had years of experience using a variety of templates which
|> >are variants of auto_ptr.  The consensus here is that auto_ptr must have
|> >ownership semantics, and they must be enforced by the assignment operator
|> >and copy constructor.

|> The requirements for exception-safe programs are very important, and I
|> agree that auto_ptr should have ownership semantics.  However, it does
|> not follow that these semantics should be implemented with the assignment
|> operator and the copy-constructor.

|> Because of the confusion, op= and the copy-ctor should be unimplemented
|> (private) and the transfer of ownership implemented with some other
|> function.  (This is also one of the Swedish Committee Draft Ballot
|> comments.)

So how do you handle transfer of ownership in return values?

I know, the question has already been asked, but IMHO, it is the
essential question.  As Bill Gibbons pointed out, you don't normally
assign/copy auto_ptr's in a block.  In real programs, all assignment and
copy is the result of a return value from a function, and special
functions don't work here.

Actually, it occurs to me that the following does work:

    auto_ptr< T >       f() ;
    auto_ptr< T >       p1( f().release() ) ;
 auto_ptr< T >  p2 ;
 p2.reset( f().release() ) ;

Somehow, it just doesn't seem natural, though.  And isn't this begging
the point.  We don't allow binding a temporary to a non-const reference,
but we have no problem calling a non-const function on it.  Where is the
difference?  (Before anyone thinks that I'm calling for a change: this
point has been discussed to death in the standards committee.  I don't
think that there is a ``correct'' solution; whatever the solution, there
will be some counter-intuitive behavior somewhere.)
--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle--
                             --Beratung in industrieller Datenverarbeitung


---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/09/30
Raw View
kanze@gabi-soft.fr (J. Kanze) writes:

>Fergus Henderson (fjh@munta.cs.mu.OZ.AU) wrote:
>|> Michael Cook <mcook@cognex.com> writes:
>
>|> >    auto_ptr<V> byValue(auto_ptr<V> a);
>|> >    a = byValue(b);
>|> >    //   warning: initialization of non-const `auto_ptr<V> &' from
>|> >    //   rvalue `auto_ptr<V>'.
>
>|> That warning is not bogus; on the contrary, the code is ill-formed,
>|> and the draft standard *requires* a diagnostic.
>|> (5.2.2/3 says that argument passing is treated as initialization,
>|> and 8.5.3/8 says you can't initialize a non-const reference with
>|> an rvalue.)
>
>To which Michael Cook (mcook@cognex.com) responded:
>
>|> Hmm.  Interesting.  That means the auto_ptr class can't be used that way.
>|> That surprises me.
>
>It not only surprises me, it bothers me profoundly. I find the
>parameter declaration of auto_ptr<T>::operator= particularly
>problematic.

Yes, I agree: I too find the auto_ptr assignment operator very disturbing,
though possibly for slightly different reasons.  I don't like the semantics
of the assignment operator: it does not just copy the auto_ptr, it
also invalidates the source of the copy.  I think that using `operator ='
for this operation is an abuse of operator overloading.

>On one hand, the right hand side of assignment definitly
>*is* modified; declaring it const (and casting away const in operator=
>is order to implement the current semantics) would be outright lying to
>the user.  It is also lying to the compiler; presumably at some future
>date, a compiler will take advantage of this const declaration in order
>to implement some fancy optimization, which will break in the case of
>casting away const.

Right.  This would awful; certainly much worse than the current situation.

>On the other hand, using an auto_ptr as the return value of a function
>would seem to be one of its principal uses.  Not allowing this would
>almost render auto_ptr useless.

I don't think so.  Instead of returning an auto_ptr<T>, you can
just return the T* obtained from a call to auto_ptr<T>::release().
The caller can use this T* to initialize another auto_ptr<T>.

>My current solution (in my personal implementation) is to declare the
>parameter of operator= const, with *big* warnings in comments at the
>point of declaration that this is a lie, and to declare the pointer
>member mutable, so as to tell the compiler that the const may be a lie.
>Hardly what I would call ideal, but the best I've been able to come up
>with til yet.

This is an awful abuse of both `mutable' and `const', and a horrible
hack, IMHO.  I wouldn't recommend it.

>Does anyone have any other solutions, or suggestions as to what the
>standard *should* say?

I think the assignment operator and copy-constructor of auto_ptr
ought to be declared private.  If you want to copy an auto_ptr,
then instead of writing

 auto_ptr<T> y = x; // copy constructor
 y = x;   // assignment operator


you should have to write

 auto_ptr<T> y = x.release();
 y = x.release();

This makes it clear that x is being modified.

--
Fergus Henderson             |  "Australia is the richest country in the world,
fjh@cs.mu.oz.au              |   according to a new system of measuring wealth
http://www.cs.mu.oz.au/~fjh  |   announced by the World Bank yesterday."
PGP: finger fjh@128.250.37.3 |  - Melbourne newspaper "The Age", 18 Sept 1995.
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]