auto_ptr explained

A

Andrew

Hello all:
After spending some time figuring out auto_ptr class' implementation, I
decided to write a small article detailing its use of the auto_ptr_ref
proxy class to enable construction and assignment from temporaries. I
have placed the manuscript at:

http://www.nd.edu/~ahenrick/auto_ptr.pdf

It develops the problem and its solution in an incremental manner,
which will hopefully make it accessible for most people without being
too tedious.

Let me know if this was of use to you,
Andrew
 
R

roberts.noah

Andrew said:
Hello all:
After spending some time figuring out auto_ptr class' implementation, I
decided to write a small article detailing its use of the auto_ptr_ref
proxy class to enable construction and assignment from temporaries. I
have placed the manuscript at:

http://www.nd.edu/~ahenrick/auto_ptr.pdf

It develops the problem and its solution in an incremental manner,
which will hopefully make it accessible for most people without being
too tedious.

This is actually a pretty interesting article. I haven't read it all
but it was going good. I've saved it in my read folder where things I
want to read go....and yes, I usually read them.
 
J

Jonathan Mcdougall

Andrew said:
Hello all:
After spending some time figuring out auto_ptr class' implementation, I
decided to write a small article detailing its use of the auto_ptr_ref
proxy class to enable construction and assignment from temporaries. I
have placed the manuscript at:

http://www.nd.edu/~ahenrick/auto_ptr.pdf

It develops the problem and its solution in an incremental manner,
which will hopefully make it accessible for most people without being
too tedious.

Let me know if this was of use to you,
Andrew

1
==
"The diffculties surrounding the auto ptr class arise primarily due to
meaning of
the const qualifier"

The main difficulty with std::auto_ptr is with ownership transfer, not
the "const
qualifier".

"// auto_ptr specialization for ints
int* dumb_ptr = new int(0);
auto_ptr i = new int(0); // i points to *dumb_ptr
auto_ptr j(i); // j points to *dumb_ptr but i points to 0"

When you give examples, try to make them valid. For small examples
(this one may
qualify), you may omit main(), but you should definitely delete
dump_ptr. And I don't
think dump_ptr adds anything to your example.

"Well, for starters, the default declaration of the copy constructor is
auto_ptr(const auto_ptr& rhs);"

This is not the "default" declaration, it is the recommended one for
most uses.

"From a language standpoint, illegal copy construction of a const auto
ptr is
something which is better supported than the legal copy construction
from a
non-const auto ptr."

This makes no sense. You could say that because C++ prohibits binding a
non-const
reference to an rvalue, having a non-const parameter in the copy
constructor limits
its use.

1.1
===
"This is to prevent programmers from unintentionally trying to
manipulate temporaries which are invalid after the end of the scoping
statement:

double& dr = std::sin(2.0); // ERROR in standard C++
dr + 2;"

This is partly wrong and too restrictive. You could say that this is to
prevent
programmers from modifying an rvalue (not a temporary) when it would
make no sense
to modify it.

The rule about references is not about temporaries, but rvalues.
rvalues are
temporaries, but not all temporaries are rvalues. For example, an
exception is a
temporary and may be bound to a non-const reference; it is not an
rvalue. You should
modify this in the whole document.

"Thus, the next statement tries to reference a variable which is
invalid;"

which is illegal, not "invalid" (which means nothing).

2
==
You examples are badly formatted. Add more spaces.

"1. This is a specialization for ints, not a template."

The word "specialization" is usually used in relation to a template, so
I would say
that this classes explicitly uses an int* as the pointee type instead.

" 3. I have added messaging to cerr to follow program execution."

Don't. Use std::clog instead.

"4. Function definitions are included in the class definition (see 2nd
footnote)."

This is usually the case actually, because std::auto_ptr is a template.

"5. I have change [..]

"changed", not "change".

2.1
===
"This is returned as a the temporary object with address ending in
ab0."

Remove the "a".

3
=
"First, in considering operator=, we note that, because assignment
changes ownership
of both arguments, it is forbidden that either argument is a const auto
ptr"

operator= only has one argument (it is not specified how the this
pointer is passed). It would be more accurate to say that it is
forbidden that either the argument or the object on which operator= is
called is const.

"auto_ptr& operator= (auto_ptr& rhs)
{
reset(rhs.release());
return *this;
std::cerr << this << "auto_ptr assigned from " << &rhs << std::endl;
}

auto_ptr& operator= (const auto_ptr_ref& rhs) throw()
{
reset(rhs.yp);
return *this;
std::cerr << this << " auto_ptr assigned from " << &rhs
<< " : auto_ptr_ref -> auto_ptr" << std::endl;
}"

Both std::cerr statements are unreachable. You would have seen it if
you had formatted
the code correctly.

"auto_ptr j();
j = source();"

j is a function returning an auto_ptr here.

4
==
"Thus far the discussion has been independent of template
meta-programming:"

Rephrase that.

"both the problem and its solution are straight forward object-oriented
C++."

Templated code is not "straight forward object-oriented C++"?


Pretty good stuff, bravo.


Jonathan
 
A

Andrew

Jonathan said:
"The diffculties surrounding the auto ptr class arise primarily due to
meaning of the const qualifier"

The main difficulty with std::auto_ptr is with ownership transfer, not
the "const qualifier".

Point taken; but this article is not directly about ownership transfer.
It is about the use of auto_ptr_ref proxy class to prevent copy
construction or copy assignment of a const auto_ptr.
"// auto_ptr specialization for ints
int* dumb_ptr = ...

When you give examples, try to make them valid....

Thanks -- corrected
"Well, for starters, the default declaration of the copy constructor is
auto_ptr(const auto_ptr& rhs);"

This is not the "default" declaration, it is the recommended one for
most uses.

I have provided a couple of references.
"From a language standpoint, illegal copy construction...

This makes no sense...

Thanks, I have changed the sentence
"This is to prevent programmers from unintentionally trying to
manipulate temporaries which are invalid after the end of the scoping
statement:

double& dr = std::sin(2.0); // ERROR in standard C++
dr + 2;"

This is partly wrong and too restrictive. You could say that this is to
prevent programmers from modifying an rvalue...

I will refrain from introducing lvalue/rvalue terms in this article
because the terms themselves do not appear to be uniformly understood
by the C++ community (just search this newsgroup for instance).
Temporary objects, are, on the other hand, trivial to understand.
The rule about references is not about temporaries, but rvalues...
You should modify this in the whole document.

I do not understand your comment here. This concept is taken almost
straight out of Stroustrup's book, as cited on pg. 98: "References to
variables and referenced to constants are distinguished because the
introduction of a temporary in the case of variable is highly
error-prone;..."

If you can point out what is wrong or too restrictive with this
statement, I will fix it. As it stands, it may not be complete, but it
certainly is correct.
"Thus, the next statement tries to reference a variable which is
invalid;"

which is illegal, not "invalid" (which means nothing).

Thanks, better worded now...
You examples are badly formatted. Add more spaces.

IMHO, they are fine. If you can make a specific suggestion, that would
help.
The word "specialization" is usually used in relation to a template,
so I would say that this classes explicitly uses an int* as the pointee
type instead.

That is true, but auto_ptr is normally implemented as a template. I
think the meaning is clear.
" 3. I have added messaging to cerr to follow program execution."

Don't. Use std::clog instead.

I think that std::cerr is better for this, since it isn't buffered.
"This is returned as a the temporary object with address ending in ab0."

Remove the "a".

I'll keep referring to the addresses using 3 letters/numbers. Prevents
speaking of address 90.
operator= only has one argument...

Thanks, see correction.
Both std::cerr statements are unreachable....

Opps... that's embarrassing. Thanks -- corrected
"auto_ptr j();

j is a function returning an auto_ptr here.
Fixed

"Thus far the discussion has been independent of template
meta-programming:"

Rephrase that.
OK

Templated code is not "straight forward object-oriented C++"?

That is the point of the sentence.
Pretty good stuff, bravo.

Jonathan

Thanks, Jonathan, for reading my article and taking the time to send
me corrections. I think that it is much more clear now.

Andrew
 
A

Andrew

Jonathan said:
The rule about references is not about temporaries, but rvalues.
rvalues are temporaries, but not all temporaries are rvalues. For example, an
exception is a temporary and may be bound to a non-const reference; it is not an
rvalue.

Jonathan:

I discovered some very interesting stuff about exceptions that explains
the behavior that you point out here. I found this information in
Scott Meyers book "More Effective C++." It is really a great book.

Item 12: Understand how throwing an exception differs from passing
a parameter or calling a virtual function.

On pg. 64-65, he writes: "A thrown object (which, as explained above,
is always
a temporary) may be caught by simple reference; it need not be caught
by
reference-to-const. Passing a temporary object to a non-const
reference parameter
is not allowed for function calls (see Item 19), but it is for
exceptions."

The reason for this seems to be related to the idea that "when you call
a function,
control eventually returns to the call site (unless the function fails
to return),
but when you throw an exception, control does not return to the throw
site."

I will add a footnote to the article to note this "exception" for
exceptions.

Sincerely,
Andrew
 
J

Jonathan Mcdougall

Andrew said:
Point taken; but this article is not directly about ownership transfer.
It is about the use of auto_ptr_ref proxy class to prevent copy
construction or copy assignment of a const auto_ptr.

I know, but it is a weird way to begin an article. You could say that
one of the difficulties in understanding the implementation of
std::auto_ptr is how it works with const instances, or something like
that.
I have provided a couple of references.

I misunderstood the statement, sorry.
I will refrain from introducing lvalue/rvalue terms in this article
because the terms themselves do not appear to be uniformly understood
by the C++ community (just search this newsgroup for instance).
Temporary objects, are, on the other hand, trivial to understand.

...but are not the same thing. The choice is yours to make: either be
correct wrt the standard, or use widespread terms. I would suggest
using the correct terms, while adding a note about the common usage (an
rvalue is commonly called a temporary...).
I do not understand your comment here. This concept is taken almost
straight out of Stroustrup's book, as cited on pg. 98: "References to
variables and referenced to constants are distinguished because the
introduction of a temporary in the case of variable is highly
error-prone;..."

This is true, but it is only an example. It may make sense for some
temporaries (such as exceptions) to be modified, so not all temporaries
may not be bound to a non-const reference.

Although it may be valid to modify some temporaries (because they are
not, for example, in a readonly area in memory), such as

void f(int &i);

int main()
{
f(int(3));
}

it is forbidden because it is usually a semantic error (if f() takes a
non-const reference, this is because it is meant to be modified and
"sent back" to the user).
If you can point out what is wrong or too restrictive with this
statement, I will fix it. As it stands, it may not be complete, but it
certainly is correct.

I should have said that it is partly wrong *because* is too
restrictive. "This is to prevent.." should at least be "This is to
prevent, for example, ...". However, temporaries created by a function
returning something else than a reference *are* rvalues, so your
example is indeed correct.

You explicitly talk about temporaries throughout the whole document,
but standard-wise, it is wrong. rvalues, not temporary objects, may not
be bound to non-const references. Some rvalues are temporaries and some
temporaries are rvalues.
IMHO, they are fine. If you can make a specific suggestion, that would
help.

auto_ptr(const auto_ptr_ref& rhs) throw() : ap(rhs.yp)
{ std::cerr << this << " auto_ptr constructed from " << &rhs
<< " : auto_ptr_ref -> auto_ptr" << std::endl; }

should be

auto_ptr(const auto_ptr_ref& rhs) throw()
: ap(rhs.yp)
{
std::cerr << this << " auto_ptr constructed from " << &rhs
<< " : auto_ptr_ref -> auto_ptr" << std::endl;
}

or something along these lines. Whitespace is important, especially in
a tutorial/article/whatever you call something meant to teach. Though
there may be differences between our coding conventions, the code
should be easy to read and, imo, it is not.
I think that std::cerr is better for this, since it isn't buffered.

By convention, std::cerr is used in case something bad happened and
must be reported. Since it is not buffered, system calls are made for
each output, making it more likely to appear but slowing the
application because system calls are usually expensives. Buffered
streams may not be flushed if the application terminates unexpectedly.

Since these log messages are not vital to the execution, they should go
to a buffered stream, such as std::cout or std::clog. Since std::cout
is usually used for "normal" output, std::clog is the way to go. It is
also easier to redirect logging and errors to different streams, such
as files or different output devices.

Note that is a convention, but, imo, conventions are better followed,
event in small programs.
I'll keep referring to the addresses using 3 letters/numbers. Prevents
speaking of address 90.

I understand that, but I don't get what the "a" is. I think "This is
returned as the temporary object with address ending in ab0" makes more
sense, but there may be something I don't understand.
That is the point of the sentence.

And what else is not part of "straight forward object-oriented C++"?


Jonathan
 
A

Andrew

I recently updated the article online.

Thanks again for your recent reply Jonathan. I will try to think of a
better opening sentence for the article. I have incorporated a lot of
your changes. Some things of which I still am uncertain are:

1. It seems to me that the standard does a pretty good job of *not*
defining precisely what lvalues and rvalues are. I have avoided
them because they are confusing -- and unnecessarily so for this
article. If you can provide me with the standards definitions of these
terms, and perhaps a way to show that the auto_ptr class needs to
make such a distinction, I will do so. Temporaries are the way that
Stroustrup describes this kind of behavior and that is good enough for
me.

2. I still do not understand why my referring to the temporary object
"ab0" is bad. Please elaborate.

3. I think that we are saying the same thing about the first sentence
in Sect. 4. I do not consider templates or generic programming a part
of OO C++. That is the point of not using templates until this point
in the article, and by the opening of Sect. 4, the role of auto_ptr_ref
has been completely explained. Thus, before I began templatizing
the class, both the problem and its solution are really finished.
My purpose is simply to show that templates really don't play a role
in getting auto_ptr to work right.

Now Sect. 4 does go over some pit-falls of templates, but they do
not affect how auto_ptr_ref is used to enable construction from
temporaries.

Does that make sense?

Sincerely,
AKH
 
J

Jonathan Mcdougall

Andrew said:
I recently updated the article online.

Please quote the message you are answering to next time.
Thanks again for your recent reply Jonathan. I will try to think of a
better opening sentence for the article. I have incorporated a lot of
your changes.

Yes, I saw them.
Some things of which I still am uncertain are:

1. It seems to me that the standard does a pretty good job of *not*
defining precisely what lvalues and rvalues are. I have avoided
them because they are confusing -- and unnecessarily so for this
article. If you can provide me with the standards definitions of these
terms,

Sections 3.10 and 12.2 do the job.
and perhaps a way to show that the auto_ptr class needs to
make such a distinction, I will do so. Temporaries are the way that
Stroustrup describes this kind of behavior and that is good enough for
me.

So be it.
2. I still do not understand why my referring to the temporary object
"ab0" is bad. Please elaborate.

Ok, we may not be talking about the same thing here. I was referring to
the "a" between the words "as" and "the".

"This is returned as a the temporary object with address ending in ab0
^^^
3. I think that we are saying the same thing about the first sentence
in Sect. 4. I do not consider templates or generic programming a part
of OO C++.

Yeah, I take back what I said. Templates are not part of
object-oriented programming. A procedural language could have
templates.
That is the point of not using templates until this point
in the article, and by the opening of Sect. 4, the role of auto_ptr_ref
has been completely explained. Thus, before I began templatizing
the class, both the problem and its solution are really finished.
My purpose is simply to show that templates really don't play a role
in getting auto_ptr to work right.

And it was a good idea since templates confuse many programmers. Using
a specific type allows one to concentrate on the problem.


Jonathan
 
F

Francis Glassborow

I know, but it is a weird way to begin an article. You could say that
one of the difficulties in understanding the implementation of
std::auto_ptr is how it works with const instances, or something like
that.


The mechanism that makes auto_ptr 'work' was invented under extreme time
pressure by a brilliant C++ programmer. It is so close to the edge of
what is allowed in C++ that even the majority of WG21 & J16 (The C++
standard's committees) were not entirely convinced that it was not over
the edge. However there was tacit agreement among implementors that they
would make it work and that a much better mechanism would be provided
for the next full version of C++.

Technically, because auto_ptr is in the Standard Library it can be made
to work by 'magic' i.e. by methods not available to ordinary
programmers.

The important thing to understand is that even though (because it is a
template) its implementation is exposed for all to see, it is not
something that the Standard's committees would encourage others to use,
or that they would happily support in future.

In general programmers are well advised to keep well away from the
bleeding edge.
 
A

Andrew

Francis said:
Technically, because auto_ptr is in the Standard Library it can be made
to work by 'magic' i.e. by methods not available to ordinary
programmers.

Hello Francis:

The reason I wrote the article was to de-mystify the auto_ptr class.
People simply kept saying "don't think about how it works; it's bad
C++ black-magic."

If you take a look at the article, it think that I have made it pretty
clear that there is nothing magic in auto_ptr; although, I do think
that both the problem and the solution are less than self-explanatory.

There is also nothing magic in the way that gcc implements auto_ptr;
its standard C++. If particular implementations of the STL do
something weird to get auto_ptr to work in a particular way, that is
there choice...

I really do think that someone can learn some very important and
interesting things by looking at the auto_ptr class; even though the
Colvin/Gibbins' trick is a bit esoteric. The article tries to
point these out as they become naturally relevant to auto_ptr's
design.

Thanks,
AKH
 
J

James Dennett

Andrew said:
Jonathan Mcdougall wrote:




I will refrain from introducing lvalue/rvalue terms in this article
because the terms themselves do not appear to be uniformly understood
by the C++ community (just search this newsgroup for instance).
Temporary objects, are, on the other hand, trivial to understand.

The prohibition on binding non-const references to rvalues
is more motivated by examples such as

void increment(int& i) { ++i; }

int main() {
short s(0);
increment(s); // must not pass int(s), or...
assert(s == 1); // would fail.
}

(or more convincing ones with user-defined types). ISTR
that in D&E, Bjarne says that such mistakes were too
common while rules allowed the code above to compile --
but my memory might be wrong, and I don't have my copy
here to check.

-- James
 
F

Francis Glassborow

I really do think that someone can learn some very important and
interesting things by looking at the auto_ptr class; even though the
Colvin/Gibbins' trick is a bit esoteric. The article tries to
point these out as they become naturally relevant to auto_ptr's
design.
The problem is that the Colvin/Gibbon's trick sits right on the boundary
of legality. Had the Standards Committee's had more time they would not
have resorted to it but it was proposed and accepted at the ballot
resolution stage (or at least effectively then, IIRC the UK had not
actually voted 'no' to the DIS but would have done if auto_ptr had not
either been withdrawn of made safer than it was at that time)

This is not the place to go into the history of auto_ptr but using a
trick such as auto_ptr_ref should only be used in extremis when a proper
redesign is not an option.
 
K

Kai-Uwe Bux

Francis said:
The problem is that the Colvin/Gibbon's trick sits right on the boundary
of legality.

Now you got me curious. In which way is that implementation bordering the
illegal?

Had the Standards Committee's had more time they would not
have resorted to it but it was proposed and accepted at the ballot
resolution stage (or at least effectively then, IIRC the UK had not
actually voted 'no' to the DIS but would have done if auto_ptr had not
either been withdrawn of made safer than it was at that time)

This is not the place to go into the history of auto_ptr but using a
trick such as auto_ptr_ref should only be used in extremis when a proper
redesign is not an option.

And why do you consider it immoral -- historical reasons about feelings of
the standard committee members aside?


Best

Kai-Uwe Bux
 
F

Francis Glassborow

Kai-Uwe Bux said:
Now you got me curious. In which way is that implementation bordering the
illegal?

The trick was designed to side-step the prohibition against two user
defined conversions used in an implicit conversion sequence. Bill
Gibbins proposed that the use of auto_ptr_ref as a way to make two
implicit conversion operations take place 'separately'. Whether this is
legitimate or not requires some careful interpretation of the Standard.
At least that was the general opinion at the time.
And why do you consider it immoral -- historical reasons about feelings of
the standard committee members aside?

I do not consider immoral, just unwise. IMO designs and their
implementations should not rely on possibly fragile interpretations of
the Standard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top