Topic: auto_ptr: final decision
Author: "greg" <dont@spam.me>
Date: 1997/11/17 Raw View
Here is the proposal for the final version of auto_ptr which is
going into the standard. Many thanks to all those who helped in
its evolution.
Doc. No.: J16/97-0090R1=WG21/N1128R1
Date: November 14, 1997
Project: Programming Language C++
Reply to: Bill Gibbons <bill@gibbons.org>
Greg Colvin <greg@imrgold.com>
Fixing auto_ptr.
The auto_ptr specified in CD-2 has proved unpopular and dangerous,
primarily because the const arguments to its copy operations make
it easy to inadvertently damage an auto_ptr via a const reference,
and because the non-owning pointer left behind by a copy is an
open invitation to dangling references. The auto_ptr& arguments
to the copy constructor and assignment operator were not const in
the CD-1 auto_ptr, but were made const to allow auto_ptr values to
be passed to and returned from functions. The C++ language now
allows a more effective solution.
We propose to restore the CD-1 auto_ptr semantics by:
- removing const from the arguments to all copy operations and
from the release() function;
- restoring the pointer-zeroing effect of release();
- restoring the reset() function; and
- adding conversion functions and a private auxiliary class to
allow auto_ptr rvalues to convert to lvalues.
Draft text to replace 20.4.5 follows.
20.4.5 Template class auto_ptr
1 Template auto_ptr holds a pointer to an object obtained via
new and deletes that object when it itself is destroyed (such
as when leaving block scope 6.7).
2 Template auto_ptr_ref holds a reference to an auto_ptr. It is
used by the auto_ptr conversions to allow auto_ptr objects to
be passed to and returned from functions.
namespace std {
template<class X> class auto_ptr {
template<class Y> struct auto_ptr_ref {};
public:
typedef X element_type;
// 20.4.5.1 construct/copy/destroy:
explicit auto_ptr(X* p=0) throw();
auto_ptr(auto_ptr&) throw();
template<class Y> auto_ptr(auto_ptr<Y>&) throw();
auto_ptr& operator=(auto_ptr&) throw()
template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
~auto_ptr() throw();
// 20.4.5.2 members:
X& operator*() const throw();
X* operator->() const throw();
X* get() const throw();
X* release() throw();
void reset(X* p=0) throw();
// 20.4.5.3 conversions:
auto_ptr(auto_ptr_ref<X>) throw();
template<class Y> operator auto_ptr_ref <Y>() throw();
template<class Y> operator auto_ptr<Y>() throw();
};
}
3 The auto_ptr provides a semantics of strict ownership. 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 behavior of the program is undefined.
20.4.5.1 auto_ptr constructors
explicit auto_ptr(X* p =0) throw();
1 Postconditions: *this holds the pointer p.
auto_ptr(auto_ptr& a) throw();
2 Effects: Calls a.release().
3 Postconditions: *this holds the pointer returned from
a.release().
template<class Y> auto_ptr(auto_ptr<Y>& a) throw();
4 Requires: Y* can be implicitly converted to X*.
5 Effects: Calls a.release().
6 Postconditions: *this holds the pointer returned from
a.release().
auto_ptr& operator=(auto_ptr& a) throw();
7 Requires: The expression delete get() is well formed.
8 Effects: reset(a.release()).
9 Returns: *this.
template<class Y> auto_ptr& operator=(auto_ptr<Y>& a) throw();
10 Requires: Y* can be implicitly converted to X*. The
expression delete get() is well formed.
11 Effects: reset(a.release()).
12 Returns: *this.
~auto_ptr() throw();
13 Requires: The expression delete get() is well formed.
14 Effects: delete get().
20.4.5.2 auto_ptr members
X& operator*() const throw();
1 Requires: get() != 0
2 Returns: *get()
X* operator->() const throw();
3 Requires: get() != 0
4 Returns: get()
X* get() const throw();
5 Returns: The pointer *this holds.
X* release() throw();
6 Returns: get()
7 Postconditions: *this holds the null pointer.
void reset(X* p=0) throw();
8 Effects: If get() != p then delete get().
9 Postconditions: *this holds the pointer p.
20.4.5.3 auto_ptr conversions
auto_ptr(auto_ptr_ref<X> r) throw();
1 Effects: Calls p->release() for the auto_ptr p that r holds.
2 Postconditions: *this holds the pointer returned from
release().
template<class Y> operator auto_ptr_ref <Y>() throw();
3 Returns: An auto_ptr_ref<Y> that holds *this.
template<class Y> operator auto_ptr<Y>() throw();
4 Effects: Calls release().
5 Returns: An auto_ptr<Y> that holds the pointer returned from
release().
Analysis of Conversion operations
There are four cases to consider: direct-initialization and copy-
initialization (8.5/14) for both same-type initialization and base-
from-derived initialization.
(1) Direct-initialization, same type, e.g.
auto_ptr<int> source();
auto_ptr<int> p( source() );
This is considered a direct call to a constructor of
auto_ptr<int>,using overload resolution. There is only one
viable constructor:
auto_ptr<int>::auto_ptr(auto_ptr_ref<int>)
which is callable using the conversion
auto_ptr<int>::operator auto_ptr_ref<int>()
which should be selected when operator overloading tries to
convert type auto_ptr<int> to auto_ptr_ref<int>.
Overload resolution succeeds. No additional copying is allowed,
so the copy constructor need not be callable.
(2) Copy-initialization, same type, e.g.
auto_ptr<int> source();
void sink( auto_ptr<int> );
main() {
sink( source() );
}
According to 8.5/14:
If the initialization is direct-initialization, or if it is
copy-initialization where the cv-unqualified version of the
source type is the same class as, or a derived class of, the
class of the destination, constructors are considered...
So this case is handled the same as the direct-initialization
case.
(3) Direct-initialization, base-from-derived, e.g.
struct Base {};
struct Derived : Base {};
auto_ptr<Derived> source();
auto_ptr<Base> p( source() );
This is similar to (1); in this case, the viable constructor is:
auto_ptr<Base>::auto_ptr(auto_ptr_ref<Base>)
which is callable using the conversion
auto_ptr<Derived>::operator auto_ptr_ref<Base>()
which should be selected when operator overloading tries to
convert type auto_ptr<Derived> to auto_ptr_ref<Base>.
Overload resolution succeeds. No additional copying is allowed,
so the copy constructor need not be callable.
(4) Copy-initialization, base-from-derived, e.g.
struct Base {};
struct Derived : Base {};
auto_ptr<Derived> source();
void sink( auto_ptr<Base> );
main() {
sink( source() );
}
This case is not similar to (2), because the sentence quoted
above from 8.5/14 does not apply. So there must be a conversion
function (operator or constructor) from the argument type to the
parameter type, and it will be used to initialize a temporary
variable. Note that this initialization process does not
involve use of a copy constructor:
The user-defined conversion so selected is called to convert the
initializer expression into a temporary, whose type is the type
returned by the call of the user-defined conversion function,
with the cv-qualifiers of the destination type.
The parameter type is auto_ptr<Base>, so there must be a
conversion from auto_ptr<Derived> to auto_ptr<Base>. The
constructor
auto_ptr<Base>::auto_ptr<Derived>(auto_ptr<Derived> &)
does not work because the argument is an rvalue. But the
conversion function
auto_ptr<Derived>::operator auto_ptr<Base>()
does work. The result of calling this conversion function is a
temporary - no copy constructor is needed.
Once the temporary has been created, the draft says:
The object being initialized is then direct-initialized from
the temporary according to the rules above.
This direct-initialization is case (1) which works.
At no time in any of these four cases is the implementation
allowed to make an unnecessary copy of an auto_ptr object.
Therefore it does not matter that the copy constructor does not
work on rvalues.
---
[ 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 ]