Topic: Cleaning auto_ptr copy semantics.
Author: richard@atheist.tamu.edu (Richard Henderson)
Date: 1996/02/01 Raw View
In article <gregorDLrrun.K2x@netcom.com>,
Greg Colvin <gregor@netcom.com> wrote:
>The smallest change I can see that preserves the semantics of strict
>ownership is to separate the concepts of "holding a pointer" and "owning
>an object", so that more than one auto_ptr can hold a pointer to an object,
>but only one auto_ptr can own the object.
The problem I see with this is that a holder cannot see when
an object goes away. With the old transfer of control semantics,
there is never a dangling pointer to an object. Dangling pointers
are, in my opinion, much more dangerous than NULL pointers.
Also, I see no difference, wrt temporaries, between your solution
and making "px" mutable. Both solutions actually change the temporary.
>Note also that reset() must go, since the idiom p.reset(q.release())
>cannot safely transfer ownership. I would happily remove get() as well,
>since its primary use may turn out to be leaking pointers.
Without get(), how do you (without moving to full reference counting,
which I think should exist _along side of_ auto_ptr) pass
auto_ptr<foo> a;
to a function bar(), and retain control of the object after bar()
terminates? The current
extern void bar(foo *);
bar(a.get());
is quite satisfactory. On the other hand
extern void bar(foo *);
foo *tmp = a.release();
bar(tmp);
a.reset(tmp);
violates one of the main reasons for having auto_ptr (cleaning up
on the way out of an exception within bar()), and
extern void bar(const auto_ptr<foo> &);
bar(a);
destroys a's object when bar returns (if nothing happens within
bar() to cause it to be destroyed sooner) under both the original
semantics and your revised semantics.
> template<class Y> auto_ptr(const auto_ptr<Y>&); |
> template<class Y> auto_ptr& operator=(const auto_ptr<Y>&); |
I do like the template members though.
r~
---
[ 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: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1996/02/01 Raw View
In article <gregorDLrrun.K2x@netcom.com> gregor@netcom.com (Greg
Colvin) writes:
|> Our working paper's auto_ptr (20.4.5) specifies a non-const copy constructor
|> and assignment operator. This specification makes functions that return
|> auto_ptr less useful than I intended, due to a language restriction on
|> modifying temporaries. Since changing the language specification seems not
|> to be an option, a change to auto_ptr is in order.
I'll admit that I was toying with the idea of a change in the language
specification. The problem does not just affect auto_ptr. I have a
number of functions for which the user typically will want to pass a
temporary, but which will modify this temporary.
While toying with this last night, it occured to me that there is a
very simple solution: add an `asLValue' function:
template< class T >
class auto_ptr
{
public :
auto_ptr< T >& asLValue()
{
return *this ;
}
// ...
} ;
This allows leaving the parameters to operator= and the copy
constructor non-const, but will permit assigning/copying the return
value of a function, by explicitly invoking `asLValue':
auto_ptr< T > f() ;
auto_ptr< T > p1( f().asLValue() ) ;
p1 = f().asLValue() ;
This gives auto_ptr the semantics I and Bill Gibbons felt important:
that the pointer is *never* released from an auto_ptr, but maintains
the definition of const desired by Fergus Henderson and others; a
const object is not modified, and a function which declares a
parameter as a const reference does not modify that parameter.
Note that in fact, the `asLValue' function is not const, and cannot be
called on a const object. It can be called on a temporary, though,
and since it returns a non-const l-value, its results can be bound to
a non-const reference (i.e.: the parameter to the copy constructor or
the assignment operator).
As this idea has just occured to me, I've not had the time to work out
all of the implications. (One thing is sure: it needs a better
name:-).) Still, I think it might be worth looking into.
--
James Kanze Tel.: (+33) 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
-- A la recherche d'une activiti dans une region francophone
---
[ 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: jbuck@Synopsys.COM (Joe Buck)
Date: 1996/02/02 Raw View
gregor@netcom.com (Greg Colvin) writes:
>The smallest change I can see that preserves the semantics of strict
>ownership is to separate the concepts of "holding a pointer" and "owning
>an object", so that more than one auto_ptr can hold a pointer to an object,
>but only one auto_ptr can own the object.
Your new proposal is a recipe for dangling pointers. What about the
following program:
main () {
auto_ptr<int> outer = new int;
{
auto_ptr<int> inner = outer; // inner now owns the int
// end of inner's scope, int is deleted
}
*outer = 1; // crash
}
Under the old rules for auto_ptr, after the copy constructor for inner,
outer's internal pointer would be null, and I could test for this
(outer.get() == 0). Under your proposed change, though, outer contains
a dangling pointer.
I don't see a way around reference counts if you want a class that
contains a pointer and can do copies and assignments using the expected
semantics and guarantees to clean up the heap object.
We could still have plain auto_ptr, but with no assignment operator and no
copy constructor, and with an explicit member function for transferring
ownership. This means that a function cannot return an auto_ptr, but it
could still return one through a reference parameter.
--
-- Joe Buck <jbuck@synopsys.com> (not speaking for Synopsys, Inc)
Work for something because it is good,
not just because it stands a chance to succeed. -- Vaclav Havel
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy is
in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Author: Eugene Lazutkin <eugene@int.com>
Date: 1996/02/03 Raw View
On Friday, January 26, 1996 9:47 AM, gregor@netcom.com (Greg Colvin) wrote:
> A simple implementation makes the semantics clear:
>
> template<class X> class auto_ptr {
> X* px;
> mutable bool owner;
> public:
> explicit auto_ptr(X* p=0) : px(p), owner(true) {}
> template<class Y>
> auto_ptr(const auto_ptr<Y>& r) : px(r.release()), owner(true) {}
> template<class Y>
> auto_ptr& operator=(const auto_ptr<Y>& r) {
> if (&r != this) {
> if (owner) delete px; else owner = true;
> px = r.release();
> }
> return *this;
> }
> ~auto_ptr() { if (owner) delete px; }
>
> X& operator*() const { return *px; }
> X* operator->() const { return px; }
> X* get() const { return px; }
> X* release() const { owner = false; return px; }
> };
Sometimes I implement copy-op?
template<class Y>
auto_ptr& operator=( Y* p ) {
if( p != px ) {
if( owner )
delete px;
else
owner = true;
px = p;
}
}
Of course this is just model suggestion. I'm not sure about constness of
a pointer. Maybe const pointers suggest that auto_ptr doesn't take ownership.
Anyway this is just a question.
Sometimes I need reallocate my object and it looks like:
auto_ptr<MYOBJECT> x( new MYOBJECT(1) );
/* ... */
x = auto_ptr<MYOBJECT>( new MYOBJECT(1) );
If x keeps a head of dynamic structure, I need to change the value of the x
when this dynamic structure is changing (growing or shrinking). To my the
current auto_ptr (and your proposal) doesn't provide an elegant solution.
IMHO, the stright simple assignment operator will work just fine.
I didn't find standard equal-operators:
template<class T>
bool operator== ( const auto_ptr<T>& l, const T* r )
{ return l.get()==r; }
template<class T>
bool operator== ( const T* l, const auto_ptr<T>& r )
{ return l==r.get(); }
template<class T>
bool operator== ( const auto_ptr<T>& l, const auto_ptr<T>& r )
{ return l.get()==r.get(); }
template<class T>
bool operator!= ( const auto_ptr<T>& l, const T* r )
{ return l.get()!=r; }
template<class T>
bool operator!= ( const T* l, const auto_ptr<T>& r )
{ return l!=r.get(); }
template<class T>
bool operator!= ( const auto_ptr<T>& l, const auto_ptr<T>& r )
{ return l.get()!=r.get(); }
It's pretty convinient. It's good if you are going to create a STL
container of auto_ptr's. In this case you can use STL's algorithms.
Otherwise each programmer should implement this functions manually.
Best regards
Eugene Lazutkin
eugene@int.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: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1996/02/05 Raw View
Eugene Lazutkin wrote:
[auto_ptr]
> It's good if you are going to create a STL
> container of auto_ptr's.
EGADS! NEVER DO THIS!!!!
The following will CRASH FOR SURE:
void f(vector<auto_ptr<X> >){}
vector<auto_ptr<X> > vx; vx.push_front(new X);
f(vx);
vx[0]; // CRASH
Copying the container transfers ownership to the NEW container.
You must NOT use FIFO/block structure here: if the new
container is destroyed before the old one, all the auto_ptrs
in the old container dangle.
Even worse, if you have an input iterator onto the container
which returns an rvalue, dereferencing it steals ownership
from the container by creating a copy -- the destruction
of which will delete the object, leaving the container's
auto_ptr dangling.
[Correct use of the idiom requires the newest copy of
an auto_ptr outlive use of older ones. This is often
achieved by assigning the newest value back to the object
it was copied from.]
--
John Max Skaller voice: 61-2-566-2189
81 Glebe Point Rd fax: 61-2-660-0850
GLEBE NSW 2037 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA email: skaller@maxtal.com.au
[ 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://reality.sgi.com/employees/austern_mti/std-c++/policy.html
]
Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1996/02/05 Raw View
Joe Buck wrote:
>
> gregor@netcom.com (Greg Colvin) writes:
> >The smallest change I can see that preserves the semantics of strict
> >ownership is to separate the concepts of "holding a pointer" and "owning
> >an object", so that more than one auto_ptr can hold a pointer to an object,
> >but only one auto_ptr can own the object.
>
> Your new proposal is a recipe for dangling pointers.
It is not Greg's current proposal which is
a recipe for disaster, it is the very notion of using
the copy operator to transfer ownership. IMHO.
> I don't see a way around reference counts if you want a class that
> contains a pointer and can do copies and assignments using the expected
> semantics and guarantees to clean up the heap object.
I agree completely.
> We could still have plain auto_ptr, but with no assignment operator and no
> copy constructor, and with an explicit member function for transferring
> ownership. This means that a function cannot return an auto_ptr, but it
> could still return one through a reference parameter.
Again, I agree completely, this was my original proposal:
TWO classes, one which was to be used simply to attach heap objects onto
the stack to allow the destructor to delegate to destroying the
heap object. This fixes the problem:
void f() {
X* x= new X;
throw 1; // WOOPS,
delete x;
}
Since C++ stack frames are not copyable, copyability
in auto_ptr is not only not required, it's dangerous. The concept
is simply to SCHEDULE deletion at the point of construction:
void f() {
auto_ptr<X> x (new X); // SCHEDULES DELETION at #1
throw 1; // OK: invokes block termination code
// #1: termination code "delete x;" scheduled
}
This leaves the problem of pointers in copyable
objects -- for which reference counting is a solution
in acyclic cases.
The problem with the "move" semantics of
the current auto_ptr is NOT the strict ownership
idiom -- it's the use of inappropriate copy syntax to
access that idiom.
The solution -- IMHO -- is to ignore and NOT USE
auto_ptr, and take up where the committee failed:
attempt to obtain consensus amoung USERS on suitable
smart pointer classes. "Practice" will then push such
accepted and "in use" classes in to the _next_ standard.
My UESTL/smart module is an attempt to provide
a coherent set of smart pointers. Mail me for a copy,
email comments welcome. [It's unclear if the charter of this
newsgroup allows for "public" comment on my "private"
attempt at standardisation. But the results will be published
in black and white by Prentice Hall. Someone has to
do this, since the committee wasn't able to. The set of
classes I have shows why the committe had trouble: more
than one class is required.]
--
John Max Skaller voice: 61-2-566-2189
81 Glebe Point Rd fax: 61-2-660-0850
GLEBE NSW 2037 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. The moderation policy is
in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Author: Randal Parsons <Randal.Parsons@btal.com.au>
Date: 1996/02/06 Raw View
Eugene Lazutkin wrote:
> I didn't find standard equal-operators:
> template<class T>
> bool operator== ( const auto_ptr<T>& l, const T* r )
> { return l.get()==r; }
> template<class T>
> bool operator== ( const T* l, const auto_ptr<T>& r )
> { return l==r.get(); }
> template<class T>
> bool operator== ( const auto_ptr<T>& l, const auto_ptr<T>& r )
> { return l.get()==r.get(); }
> template<class T>
> bool operator!= ( const auto_ptr<T>& l, const T* r )
> { return l.get()!=r; }
> template<class T>
> bool operator!= ( const T* l, const auto_ptr<T>& r )
> { return l!=r.get(); }
> template<class T>
> bool operator!= ( const auto_ptr<T>& l, const auto_ptr<T>& r )
> { return l.get()!=r.get(); }
> It's pretty convinient. It's good if you are going to create a STL
> container of auto_ptr's. In this case you can use STL's algorithms.
> Otherwise each programmer should implement this functions manually.
I agree. I'd like to treat an auto_ptr similarly to a normal pointer.
This means that operator== and operator!= need to be defined. I'd
also like to be able to test if its null or not - using operator!()
and operator int().
Randal Parsons.
[ 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://reality.sgi.com/employees/austern_mti/std-c++/policy.html
]
Author: Eugene Lazutkin <eugene@int.com>
Date: 1996/02/08 Raw View
On Sunday, February 04, 1996 6:09 PM,
John Max Skaller <maxtal@suphys.physics.su.oz.au> wrote:
> Eugene Lazutkin wrote:
> [auto_ptr]
>
> > It's good if you are going to create a STL
> > container of auto_ptr's.
>
> EGADS! NEVER DO THIS!!!!
>
> The following will CRASH FOR SURE:
>
> void f(vector<auto_ptr<X> >){}
> vector<auto_ptr<X> > vx; vx.push_front(new X);
You're right. push_front() for vector<> is not good idea! :-)
> f(vx);
This is a very time-consuming operation. You should use it if it
is really essential for you.
> vx[0]; // CRASH
Right. I have a shorter example to demonstrate the same behavior.
It doesn't use STL at all!
// declarations
typedef auto_ptr<T> aptr;
void f( aptr );
// example
aptr p( new T );
f( p );
*p; // CRASH
What's your point? I agree with your that non-reference counting
(or non-garbage collecting) pointers are dangerous. I mean both
C-style old-fashioned pointers and current auto_ptrs. But you can
write a program which doesn't violate certain restrictions.
To me auto_ptr is useful under certain circumstances. It's better
than nothing. At least in this case I can control the life of the
pointed object (if I write my program correctly). Garbage collection
doesn't guarantee anything.
It's sad that reference counting was rejected by the committee.
Maybe it was a better choice.
> Copying the container transfers ownership to the NEW container.
> You must NOT use FIFO/block structure here: if the new
> container is destroyed before the old one, all the auto_ptrs
> in the old container dangle.
>
> Even worse, if you have an input iterator onto the container
> which returns an rvalue, dereferencing it steals ownership
> from the container by creating a copy -- the destruction
> of which will delete the object, leaving the container's
> auto_ptr dangling.
You have the same problems without any containers.
> [Correct use of the idiom requires the newest copy of
> an auto_ptr outlive use of older ones. This is often
> achieved by assigning the newest value back to the object
> it was copied from.]
Eugene Lazutkin
eugene@int.com
---
[ comp.std.c++ is moderated. Submission address: std-c++@ncar.ucar.edu.
Contact address: std-c++-request@ncar.ucar.edu. Moderation policy:
http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]
Author: Stan Sulsky <sjs@curtech.com>
Date: 1996/02/09 Raw View
austern@isolde.mti.sgi.com (Matt Austern) wrote:
[...]
>... I'm beginning to think that the right decision is to
>restrict auto_ptr to thas one simple use, instead compromising its
>safety by trying to make it useful for other purposes as well.
[..]
I agree completely. IMO, auto_ptr has become burdened with a set
of conflicting requirements, which no amount of tinkering can
satisfy.
--stan
--
Stan Sulsky
Current Technology, Inc. | -- sjs@curtech.com --
97 Madbury Rd. | (603) 868-2270 - voice
Durham, NH 03824 USA | (603) 868-1352 - fax
[ 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://reality.sgi.com/employees/austern_mti/std-c++/policy.html
]