Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

auto_ptr_ref

30 views
Skip to first unread message

Neelesh Bodas

unread,
Nov 22, 2005, 3:55:14 AM11/22/05
to
Hello All,
My question is regarding the implementation of auto pointers - How
does the class auto_ptr_ref work?

eg: suppose I have :

auto_ptr<int> foo();

int bar()
{
auto_ptr<int*> k = foo();
}

The exact sequence of calls as I see here is (no optimizations assumed)
-
1) foo returns auto_ptr<int> by value
2) CC is called on the return value.
3) The returned auto_ptr<int> is converted to auto_ptr_ref<int> by
using operator auto_ptr_ref
4) Now since this is a copy initialization and not a direct
initialization, the RHS (whose type is now auto_ptr_ref<int>) needs to
get converted to auto_ptr<int> again - done by ctor of auto_ptr<int>
which takes auto_ptr_ref<int>
5) Now CC should be called - but again we are back to old problem - the
generated auto_ptr is temporary and hence cannot be passed as a
non-const reference to the function.

Seems I am going somewhere in cycle :-(

Josuttis' book doesnot say enough on this -

<quote>
"The mechanism relies on a slight difference between the overloading
and template argument deduction rules. This difference is too subtle to
be of use as a general programming tool, but it is sufficient to enable
the auto_ptr class to work correctly."
</quote>

Can somebody explain what I am not getting here?

Alf P. Steinbach

unread,
Nov 22, 2005, 4:12:31 AM11/22/05
to
* Neelesh Bodas:

>
> eg: suppose I have :
>
> auto_ptr<int> foo();
>
> int bar()
> {
> auto_ptr<int*> k = foo();
> }
>
> The exact sequence of calls as I see here is (no optimizations assumed)
> -
> 1) foo returns auto_ptr<int> by value

OK.


> 2) CC is called on the return value.

No, there is no std::auto_ptr( std::auto_ptr const& ) copy constructor.


> 3) The returned auto_ptr<int> is converted to auto_ptr_ref<int> by
> using operator auto_ptr_ref

OK.


> 4) Now since this is a copy initialization and not a direct
> initialization, the RHS (whose type is now auto_ptr_ref<int>) needs to
> get converted to auto_ptr<int> again - done by ctor of auto_ptr<int>
> which takes auto_ptr_ref<int>

OK.


> 5) Now CC should be called

No, you've reached the goal.


> - but again we are back to old problem - the
> generated auto_ptr is temporary and hence cannot be passed as a
> non-const reference to the function.

What generated auto_ptr? The one generated is the one you've named 'k'.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Neelesh Bodas

unread,
Nov 22, 2005, 4:41:19 AM11/22/05
to
Hi Alf, Thanks for the help.

Actually, more I think on this, more confusion it is creating.

> >
> > auto_ptr<int> foo();
> >
> > int bar()
> > {
> > auto_ptr<int*> k = foo();
> > }

Isn't is correct that whever a function returns by value, a CC must get
called on the return value(Though it might be optimized away by the
compiler?)

>
> No, there is no std::auto_ptr( std::auto_ptr const& ) copy constructor.
>

I believe that we do not need a std::auto_ptr( std::auto_ptr const& )
copy constructor - std::auto_ptr( std::auto_ptr& ) would do the work -
since the source of the copy inside "foo" is not necessarily a const :

std::auto_ptr<int> foo()
{
std::auto_ptr<int> v;
return v; // a copy ctor will be called on v since this is return by
value
}

But when I debugged throgh std::auto_ptr code I didnot find any call to
the given copy constructor (as you said in the reply.)

I am not getting where exactly I am going wrong.

Alf P. Steinbach

unread,
Nov 22, 2005, 5:48:14 AM11/22/05
to
* Neelesh Bodas:

> Hi Alf, Thanks for the help.
>
> Actually, more I think on this, more confusion it is creating.
>
> > >
> > > auto_ptr<int> foo();
> > >
> > > int bar()
> > > {
> > > auto_ptr<int*> k = foo();
> > > }
>
> Isn't is correct that whever a function returns by value, a CC must get
> called on the return value(Though it might be optimized away by the
> compiler?)

No. _Some_ constructor must be called. That call can be optimized
away, but the constructor must be available to be called (the as-if rule
for optimization).


> > No, there is no std::auto_ptr( std::auto_ptr const& ) copy constructor.
> >
>
> I believe that we do not need a std::auto_ptr( std::auto_ptr const& )
> copy constructor - std::auto_ptr( std::auto_ptr& ) would do the work -
> since the source of the copy inside "foo" is not necessarily a const :
>
> std::auto_ptr<int> foo()
> {
> std::auto_ptr<int> v;
> return v; // a copy ctor will be called on v since this is return by
> value
> }
>
> But when I debugged throgh std::auto_ptr code I didnot find any call to
> the given copy constructor (as you said in the reply.)

That is unfortunately a different effect.

Here the reference-to-non-const copy constructor could be called, and I
believe _is_ called at the formal level (the C++ abstract machine
level), but that call is optimized away by your compiler.

However, declare 'v' as const, or use a temporary, and you're no longer
in possible copy constructor call territory: _some_ constructor must be
called (although it can be optimized away), but in the case of const or
temporary auto_ptr that constructor isn't the copy constructor.


> I am not getting where exactly I am going wrong.

This time it was mistaking the compiler's optimization as confirmation
of a misinterpretation of what I wrote (sorry about not being more
clear). I didn't mean that a copy constructor is never called for any
function result. I meant that a copy constructor does not always have
to be called, and in particular I meant, and wrote, that std::auto_ptr
doesn't have a ref-to-const copy constructor, which is the usual copy
constructor used for a function result.

Neelesh Bodas

unread,
Nov 22, 2005, 6:36:30 AM11/22/05
to
Hello Alf,

Alf P. Steinbach wrote:

> No. _Some_ constructor must be called. That call can be optimized
> away, but the constructor must be available to be called (the as-if rule
> for optimization).

Thanks, this clears my misunderstanding.

Continuing on my previous mail, we see that there are _two_ user
defined conversions going on one after the other in a single expression
-

1) std::auto_ptr<int> to std::auto_ptr_ref<int> by a call to
std::auto_ptr<int>operator auto_ptr_ref<int>()
2) Call to the converting constructor
std::auto_ptr<int>(std::auto_ptr_ref<int>) to create the result of type
auto_ptr<int>

If I understand right, since both of these are user defined
conversions, only one of them is allowed.

Could you please comment on this?

Alf P. Steinbach

unread,
Nov 22, 2005, 7:27:31 AM11/22/05
to
* Neelesh Bodas:
> * Alf P. Steinbach:

I'd rather not... ;-)

Because it's an issue that people tend to have to agree to disagree on.

My personal take is that the second doesn't really count as a user
defined conversion because it's not a conversion producing a temporary;
it's like plugging a char const* into a std::string constructor with
direct initialization syntax (see below); no conversion has to be found
by the compiler, it's a direct match.

But that is just a convenient shovel-it-under-the-carpet-and-wave-
yer-hands-frantically explanation, because if you do

class Base {};
class Derived: public Base {};

std::auto_ptr<Base> x = std::auto_ptr<Derived>( new Derived );

then you _are_ up against the single conversion rule, and the only thing
different seems to be use of a templated constructor.

On the other hand (or is it the third, now), if you write the
initialization as a direct initialization,

std::auto_ptr<Base> x( std::auto_ptr<Derived>( new Derived ) );

then with a standard-conforming compiler all is a good (note here: MSVC
7.1 is not a standard-conforming compiler in this respect).

Perhaps someone who Really Really Knows could be kind enough to comment
on the formal "why" your point (2) isn't counted in the number of
user-defined conversions involved -- or perhaps that it is but that
the standard library is allowed to do anything, whichever is the case.

Neelesh Bodas

unread,
Nov 22, 2005, 7:54:34 AM11/22/05
to
Alf P. Steinbach wrote:

> then you _are_ up against the single conversion rule, and the only thing
> different seems to be use of a templated constructor.

I observed a similar behaviour with a non-templated constructor also -

class Y { } ;
class X {
public:
X() { }
X (X&) { } // copy constuctor takes non-const reference
operator Y();
X(Y) { }
} ;

X foo() { X p; return p; }

int main()
{
X k = foo();
}

Here -
1) foo returns an object of type X by value. This is a temporary.
2) since this temporary cannot be passed as a reference-to-non-const,
hence the copy constructor is of no use here
3) So operator Y() is called on this temporary, and it returns another
object of type Y.
4) Now again the converting constructor X(Y) will be called and the
resulting value is put into k.

Since this is a copy initialization (and not a direct initialization),
I was thinking that this involved _two_ calls to user defined
conversion operators. But still this compiles (Comeau online, g++
3.4.2)

Aleksey Loginov

unread,
Nov 22, 2005, 8:05:31 AM11/22/05
to

Neelesh Bodas wrote:
> Hello All,
> My question is regarding the implementation of auto pointers - How
> does the class auto_ptr_ref work?
>
> eg: suppose I have :
>
> auto_ptr<int> foo();
>
> int bar()
> {
> auto_ptr<int*> k = foo();
> }
>

as you know, it's not possible to pass RHS "foo()" as non const
reference "auto_ptr &".
but you can call it's not const methods "foo().nonconst()".
so, "auto_ptr" use that fact to allow constructor modify RHS.

as illustration
#include<iostream>

struct auto_ptr {
int n;
static int next () { static int i; return ++i; }

struct auto_ptr_ref {
auto_ptr * ptr;
auto_ptr_ref ( auto_ptr * p ) : ptr (p) {}
};

auto_ptr () : n (next()) { std::cout << this << ":auto_ptr():new(" <<
n << ")" << std::endl; }
~auto_ptr () { std::cout << this << ":auto_ptr():delete(" << n << ")"
<< std::endl; }
auto_ptr ( auto_ptr & );
auto_ptr & operator = ( auto_ptr & );

auto_ptr ( auto_ptr_ref /*by value*/ r ) {
this->n = r.ptr->n;
r.ptr->n = 0; // update RHS
std::cout << this << ":auto_ptr(auto_ptr_ref):" << r.ptr <<
std::endl;
}
operator auto_ptr_ref () {
std::cout << this << ":operator auto_ptr_ref():" << std::endl;
return auto_ptr_ref(this);
}

};

auto_ptr foo () { auto_ptr ptr; return ptr; }

int main(){
auto_ptr ptr = foo ();
}

Neelesh Bodas

unread,
Nov 22, 2005, 11:13:50 AM11/22/05
to
Hi Aleksey,
thanks for the reply.

Aleksey Loginov wrote:

> auto_ptr ( auto_ptr_ref /*by value*/ r ) {
> this->n = r.ptr->n;
> r.ptr->n = 0; // update RHS
> std::cout << this << ":auto_ptr(auto_ptr_ref):" << r.ptr <<
> std::endl;
> }
> operator auto_ptr_ref () {
> std::cout << this << ":operator auto_ptr_ref():" << std::endl;
> return auto_ptr_ref(this);
> }
>
> };
>
> auto_ptr foo () { auto_ptr ptr; return ptr; }
>
> int main(){
> auto_ptr ptr = foo ();
> }

The basic reason why I am not able to understand why this works is that
I observe _two_ user defined conversions applied here -
1) conversion from auto_ptr to auto_ptr_ref using operator auto_ptr_ref
() : applied on return value of foo
2) conversion from auto_ptr_ref to auto_ptr using the constructor
auto_ptr ( auto_ptr_ref) applied on the result of 1.

The reason why I am considering (2) as a conversion is that the
initialization of ptr is done as a copy initialization - not a direct
initialization.

Or may be it is the case that (2) above is _not_ considered as a
conversion by the compiler somehow. Not sure.

Hence the confusion.

mlimber

unread,
Nov 22, 2005, 1:16:55 PM11/22/05
to
Neelesh Bodas wrote:
> Hello All,
> My question is regarding the implementation of auto pointers - How
> does the class auto_ptr_ref work?
[snip]

See these links by N. Josuttis and S. Meyers, respectively:

http://www.josuttis.com/libbook/auto_ptr.html

http://www.awprofessional.com/content/images/020163371X/autoptrupdate%5Cauto_ptr_update.html

Cheers! --M

Neelesh Bodas

unread,
Nov 22, 2005, 1:35:38 PM11/22/05
to
Thanks mlimber for the reply.

Sorry for going over the same problem again, but it is really leaving
me in a confused state. Here is another confusion over the
"auto_ptr_ref" concept :

Stroustrup says in TC++PL (14.4.2) :
"The purpose of auto_ptr_ref is to implement the destructive copy
semantics for ordinary auto_ptrs while making it impossible to copy a
const auto_ptr"

Josuttis says something similar in "The C++ Std Library" -
"Fortunately, there was a late design decision that made auto_ptrs less
dangerous. By some tricky implementation techniques, transfer of
ownership is not possible with constant references. In fact, you can't
change the ownership of any constant auto_ptr:"

I havenot understood this at all. I understand that use of
auto_ptr_ref is to be able return auto_ptrs from functions and use it
on RHS or use it as argument to another function. Where does this
"Destructive copy semantics" and "impossibility to copy auto_ptrs" come
into picture? (Basically that is already guaranteed when the copy
constructor takes non-const reference as argument)

Can somebody please enlighten me on this? (It is very well possible
that I have missed out some basic points due to excessive thinking :-(
.) I have searched the net all the way, and I have already gone
through all the three drafts of auto_ptr implementation (along with the
links given by you). But I was unable to find any convincing
explanation to these issues.

Any reference which discusses design-goals of auto_ptr_ref in details
(along with examples - not just a theoretical discussion) will also be
useful.

mlimber

unread,
Nov 22, 2005, 2:08:58 PM11/22/05
to

Destructive copy semantics means that passing an auto_ptr around
transfers ownership, and that is precisely what distinguishes it from
boost::scoped_ptr. To achieve this design goal, Bill Gibbons and Greg
Colvin proposed the "tricky implementation" of using conversions to
auto_ptr_ref detailed at the end of the second link. Please see their
discussion in the section "Analysis of Conversion operations". If you
have specific questions about it, feel free to ask.

Cheers! --M

Aleksey Loginov

unread,
Nov 23, 2005, 12:38:49 AM11/23/05
to

Neelesh Bodas wrote:
> Hi Aleksey,
> thanks for the reply.
>
> Aleksey Loginov wrote:
>
> > auto_ptr ( auto_ptr_ref /*by value*/ r ) {
> > this->n = r.ptr->n;
> > r.ptr->n = 0; // update RHS
> > std::cout << this << ":auto_ptr(auto_ptr_ref):" << r.ptr <<
> > std::endl;
> > }
> > operator auto_ptr_ref () {
> > std::cout << this << ":operator auto_ptr_ref():" << std::endl;
> > return auto_ptr_ref(this);
> > }
> >
> > };
> >
> > auto_ptr foo () { auto_ptr ptr; return ptr; }
> >
> > int main(){
> > auto_ptr ptr = foo ();
> > }
>
> The basic reason why I am not able to understand why this works is that
> I observe _two_ user defined conversions applied here -
> 1) conversion from auto_ptr to auto_ptr_ref using operator auto_ptr_ref
> () : applied on return value of foo
> 2) conversion from auto_ptr_ref to auto_ptr using the constructor
> auto_ptr ( auto_ptr_ref) applied on the result of 1.
>

I try to explain.
When foo returns ptr, compiler may call "auto_ptr ( auto_ptr & )" (1
step)
or "auto_ptr ( auto_ptr_ref & )" through "operator auto_ptr_ref ()" (2
steps).
First case preferred, because less steps must be done. (see Andrey's
post
http://groups.google.com/group/comp.lang.c++/browse_frm/thread/464a47959afdddf7)
But in real, compiler allowed (and it is) not to do all that calls, and
return ptr as RHS.

> The reason why I am considering (2) as a conversion is that the
> initialization of ptr is done as a copy initialization - not a direct
> initialization.
>
> Or may be it is the case that (2) above is _not_ considered as a
> conversion by the compiler somehow. Not sure.
>
> Hence the confusion.

Hope this help. Sorry for dummy language.

Neelesh Bodas

unread,
Nov 23, 2005, 1:21:23 AM11/23/05
to
Hello mlimber,

> Please see their discussion in the section "Analysis of Conversion operations". If you
> have specific questions about it, feel free to ask.

Thanks a lot for this. I read the link again, and it seems that my all
doubts have finally been answered satisfactorily.
So just thought of answering them here for others also. (please do
correct if my explanation is not appropriate)

> Neelesh Bodas wrote:
> The basic reason why I am not able to understand why this works is that
> I observe _two_ user defined conversions applied here -
> 1) conversion from auto_ptr to auto_ptr_ref using operator auto_ptr_ref
> () : applied on return value of foo
> 2) conversion from auto_ptr_ref to auto_ptr using the constructor
> auto_ptr ( auto_ptr_ref) applied on the result of 1.

(2) is _not_ a conversion in this case. Quoting 8.5p14 -

"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."

Hence in the current case, when we say
std::auto_ptr<int> k = foo();
the source type ( = return type of foo() ) is std::auto_ptr<int> and
the destination type is also the same. Hence an constructor will be
considered. The only suitable constructor is
std::auto_ptr::auto_ptr(std::auto_ptr_ref).
This constructor can be called by doing a conversion from std::auto_ptr
to std::auto_ptr_ref using std::auto_ptr::operator auto_ptr_ref()
Hence there is only one user defined conversion involved, and the
constructor is called on the temporary that is the result of the
conversion.

>Neelesh Bodas wrote:
> 4) Now since this is a copy initialization and not a direct
> initialization, the RHS (whose type is now auto_ptr_ref<int>) needs to
> get converted to auto_ptr<int> again - done by ctor of auto_ptr<int>
> which takes auto_ptr_ref<int>

No, that is not correct.Note that the copy initialization has source
and destination types (cv -unqualified) same - and this is treated as a
special case, and it is considered differently (8.5p14), and an
initialization is possible without a call to a copy constructor.

> 5) Now CC should be called - but again we are back to old problem -

So no unnecessary calls to copy constructor are made in this case.

> Neelesh Bodas wrote:
> I observed a similar behavior with a non-templated constructor also -


> int main()
> {
> X k = foo();
> }

The same story here.

Thanks a lot to Alf, mlimber and Aleksey for their invaluable help and
insights in the problem.

Howard Hinnant

unread,
Nov 23, 2005, 3:45:08 PM11/23/05
to
In article <43830983....@news.individual.net>,

al...@start.no (Alf P. Steinbach) wrote:

> Perhaps someone who Really Really Knows could be kind enough to comment
> on the formal "why" your point (2) isn't counted in the number of
> user-defined conversions involved -- or perhaps that it is but that
> the standard library is allowed to do anything, whichever is the case.

I'm just catching up on my news reading and read through this thread
with some amusement. And I won't pretend to be able to follow all of
the twists and turns of the standard as the three of you have just done,
though I have studied it intently in the past (and perhaps even
understood it for awhile).

My reason for posting is just to point out that auto_ptr has been a very
valuable (but failed) experiment. auto_ptr has been the pioneer of
"move semantics". It is my hope that a much simpler form of move
semantics will appear in C++0X, including a (yet again) redesigned
auto_ptr under the name of unique_ptr. The new unique_ptr will do
everything auto_ptr does now (without auto_ptr_ref tricks) except one
thing: It will not move from an lvalue:

unique_ptr<int> p1;
unique_ptr<int> p2 = p1; // compile time error

However it will move from an rvalue:

unique_ptr<int> foo();

int bar()
{
unique_ptr<int> k = foo(); // ok
}

For more details see:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#20.4.5
%20-%20Class%20template%20auto_ptr

(watch the wrap)

The most exciting thing about this (imho) is that unique_ptr is so
simple that you will easily be able to mimic its behavior in your own
classes (presumably the reason for devoting such study to auto_ptr). In
a nutshell, this is the design of unique_ptr:

template <class T>
class unique_ptr
{
public:
...
unique_ptr(unique_ptr&& u); // rvalues bind here
...
private:
unique_ptr(const unique_ptr&); // lvalues bind here
};

Once you get past the rvalue reference (&&), things just get really
simple.

-Howard

0 new messages