Topic: proposal: fix auto_ptr
Author: Ian Haggard <ian@shellus.com>
Date: 1997/01/29 Raw View
With many acknowledgements to Valentin Bonnard, I would like to propose
a fix for auto_ptr that would allow it to pretty much become its old
self again. This is basically just a fleshing out of M. Bonnard's
auto_objet class he describes in his french tutorial, which is on the
www at http://www.pratique.fr/~bonnardv/guide_cpp.html, so any good
ideas contained herein are all his, and any bad ideas contained herein
are probably mine.
Here's what I would do to auto_ptr. Please note, however, that since
the code below was tested on g++ 2.7.0 I was unable to declare any of
the templated member functions as defined by the standard. And I
thought it better to post code that I had tested myself rather than code
that I hadn't been able to test. So please mentally replace member
functions with templated member functions as necessary:
#include <assert.h>
#include <stddef.h>
template<class X> class auto_ptr_retval {
X* px;
public:
explicit auto_ptr_retval(X* p =NULL) : px(p) {}
~auto_ptr_retval() { if ( px ) delete px; }
X* release() { X* tmp=px; px=NULL; return tmp; }
private:
auto_ptr_retval& operator=(const auto_ptr_retval<X>&);
auto_ptr_retval(const auto_ptr_retval<X>& ap);
};
template<class X> class auto_ptr {
X* px;
public:
// _lib.auto.ptr.cons_ construct/copy/destroy:
explicit auto_ptr(X* p =NULL) : px(p) {}
auto_ptr(auto_ptr<X>& ap) :px(ap.release()) {}
auto_ptr(const auto_ptr_retval<X>& ap)
:px(const_cast<auto_ptr_retval<X>&>(ap).release()) {}
void operator=(auto_ptr<X>& ap) { if ( this!=&ap) reset(ap.release());
}
void operator=(const auto_ptr_retval<X>& ap)
{ reset(const_cast<auto_ptr_retval<X>&>(ap).release()); }
~auto_ptr() { reset(); }
// _lib.auto.ptr.members_ members:
X& operator*() const { assert(px); return *px; }
X* operator->() const { assert(px); return px; }
X* get() const { return px; }
X* release() { X* tmp=px; px=NULL; return tmp; }
X* reset(X* p =NULL) { if( px && px!=p ) delete px; return (px=p); }
operator bool() { return px; }
};
template<class X>
auto_ptr_retval<X> return_auto_ptr(auto_ptr<X> px)
{ return auto_ptr_retval<X>(px.release()); }
These changes would enable to following code to work:
#include <auto_ptr.h>
#include <stream.h>
// this works, too, but I would prefer to use the return_auto_ptr()
function
// auto_ptr_retval<int> new_int(int v)
// { return auto_ptr_retval<int>(new int(v)); }
auto_ptr_retval<int> new_int(int v)
{ return return_auto_ptr(auto_ptr<int>(new int(v))); }
void print_int(const auto_ptr<int>& i) { cout << *i << "\n"; }
void change_int(auto_ptr<int>& i,int v) { i=new_int(v); }
void destroy_int(auto_ptr<int> i) { cout << "Destroying "; print_int(i);
}
main() {
new_int(1);
print_int(new_int(2));
// change_int(new_int(3),4); // error -- init non-const reference w/
temporary
auto_ptr<int> i=new_int(5);
change_int(i,6);
destroy_int(i);
destroy_int(new_int(7));
}
I think that M. Bonnard's solution is much better than the one currently
in the standard and I'd like to put it up for consideration. It has
several big advantages over the current proposal in the standard.
Before the changes to it, auto_ptr was a simple class -- one auto_ptr
owned an object, and that was the only auto_ptr that had a copy of that
object. There was no ownership bit necessary, no worrying about whether
or not a particular auto_ptr really owned the object it pointed to.
Passing an auto_ptr meant that you were passing ownership to the object
it pointed to and that that object was guaranteed to be around as long
as the auto_ptr pointed to it. The new auto_ptr gives no such
guarantees.
In the presence of threads, for example, an object being pointed to by
multiple auto_ptr's in different threads could disappear in the middle
of a function in one thread because the auto_ptr in the other thread --
the auto_ptr that actually owned the object -- deleted the object. Not
that I am encouraging the use of the auto_ptr class in such a way. But
the standard HAS just enabled such a use of auto_ptr in the name of
allowing auto_ptr's to be returned from functions.
Now I the standards designers would say that auto_ptr was not intended
to be used in such situations. To quote from the standard:
> The auto_ptr provides a semantics of strict ownership. After initial
> construction an auto_ptr owns the object it holds a pointer to. Copying
> an auto_ptr copies the pointer and transfers ownership to the
> destination. If more than one auto_ptr owns the same object at the same
> time the behaviour of the program is undefined.
But if you are going to say the behavior is undefined, why not make it a
compile-time error? Unfortunately, there is absolutely no way for the
compiler to detect this undefined behavior with the auto_ptr
implementation in the standard.
But M. Bonnard's modifications retain the features of the original
auto_ptr, which allowed compile-time rejection of this undefined
behavior. And the cost is very small -- when you want to return an
auto_ptr from a function, just change its return type to be an
auto_ptr_retval and use the return_auto_ptr() function in each return
statement in the function. Sure, programmers could try to use the
auto_ptr_retval class in places other than return statements, but such a
use would be easily detectable and the interface offered by the
auto_ptr_retval class is sufficiently restrictive that no one would want
to use it anywhere else, anyway.
--
Ian Haggard || ian@shellus.com (work) || IanHaggard@juno.com (home)
Linux -- "Oh, no, Mr Bill!" || #define employer_opinion !my_opinion
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]