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

Sematic of constructor in a return statement

0 views
Skip to first unread message

Micha S. Berger

unread,
Mar 3, 1997, 3:00:00 AM3/3/97
to

[Moderator's note: this is crossposted to comp.lang.c++.moderated and
comp.std.c++. If you follow up, please think about which group your
reply belongs in. mha]

We all know you can't do
int& foo() {
int a = 3;
return a;
}

So, on a job interview I was asked about how to return a reference,
without invoking new -- since new might lead to a memory link on the
part of an unaware user of your class/library.

(The case, I forget the details, involved nesting <<, so that you wanted
the left side to actually refer to the same object.)

Either way, after giving up (mumbling something about g++'s workaround),
the interview claims that according to CD2,
return Foo(x);
will construct a Foo on the CALLER'S stack, not on the current
function's.

Sounded unlikely to me. Anyone see this written anywhere?

--
Micha Berger 201 916-0287 Help free Ron Arad, held by Syria 3727
days!
mi...@aishdas.org (16-Oct-86 - 27-Feb-97)
For a mitzvah is a candle, and the Torah its light.
http://aishdas.org -- Orthodox Judaism: Torah, Avodah, Chessed

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std...@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++...@ncar.ucar.edu
]

Fergus Henderson

unread,
Mar 4, 1997, 3:00:00 AM3/4/97
to

[Note: this is crossposted to comp.lang.c++.moderated and comp.std.c++.
If you follow up, please think about which group your reply belongs in.]

"Micha S. Berger" <ais...@IDT.NET> writes:

>We all know you can't do
>int& foo() {
> int a = 3;
> return a;
>}
>
>So, on a job interview I was asked about how to return a reference,
>without invoking new -- since new might lead to a memory link on the
>part of an unaware user of your class/library.

[...]
>the interview[er] claims that according to CD2,


> return Foo(x);
>will construct a Foo on the CALLER'S stack, not on the current
>function's.

As stated, this is not correct. The function

struct Foo { };
Foo & foo (Foo & x) {
return Foo(x);
}

has undefined behaviour, because lifetime of the temporary object Foo(x)
ends at the end of the expression, so the function will return a
dangling
reference.

>(The case, I forget the details, involved nesting <<, so that you wanted
>the left side to actually refer to the same object.)

A function such as

ostream & operator << (ostream & o, Foo x) {
...
return o;
}

would be fine. Perhaps that was what your interviewer meant.

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
---

[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std...@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++...@ncar.ucar.edu ]

Paul Black

unread,
Mar 4, 1997, 3:00:00 AM3/4/97
to

f...@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
> [Note: this is crossposted to comp.lang.c++.moderated and comp.std.c++.
> If you follow up, please think about which group your reply belongs in.]
>
> [snip]

> The function
>
> struct Foo { };
> Foo & foo (Foo & x) {
> return Foo(x);
> }
>
> has undefined behaviour, because lifetime of the temporary object Foo(x)
> ends at the end of the expression, so the function will return a
> dangling
> reference.

If this is correct, then section 12.2, paragraph 5 of the draft is
wrong.

Paul
---

James Kanze

unread,
Mar 4, 1997, 3:00:00 AM3/4/97
to

"Micha S. Berger" <ais...@IDT.NET> writes:

|> [Moderator's note: this is crossposted to comp.lang.c++.moderated and

|> comp.std.c++. If you follow up, please think about which group your

|> reply belongs in. mha]


|>
|> We all know you can't do
|> int& foo() {
|> int a = 3;
|> return a;
|> }
|>
|> So, on a job interview I was asked about how to return a reference,
|> without invoking new -- since new might lead to a memory link on the
|> part of an unaware user of your class/library.
|>

|> (The case, I forget the details, involved nesting <<, so that you wanted
|> the left side to actually refer to the same object.)
|>

|> Either way, after giving up (mumbling something about g++'s workaround),

|> the interview claims that according to CD2,


|> return Foo(x);
|> will construct a Foo on the CALLER'S stack, not on the current
|> function's.
|>

|> Sounded unlikely to me. Anyone see this written anywhere?

Sounds like the interviewer is somewhat confused. First, of course, I'm
not sure what he means by the "caller's" stack; all of the
implementations I know have just one stack. I suppose he means in the
caller's stack frame (not a C++ concept, but the usual implementation).

Where the implementation constructs the temporary is not defined. What
is required is:

1. That its destructor be called *before* returning to the calling
expression (under the lifetime of temporaries rule), and

2. That the memory be reclaimed somehow or somewhere. (This is not
actually required by the standard. An implementation which didn't do
so, however, would be totally useless.)

Note that point 1 is required by the standard; it is forbidden for a
conforming implementation to make the above work.

In practice, all of the implementations I've seen construct the
temporary in the callee stack frame, and most of the implementations
I've seen will generate a warning about returning a reference to a
temporary in the above case.

I suspect that in fact, the interviewer was referring to paragraph 5 of
section 12.2:

The second context is when a reference is bound to a temporary. The
temporary to which the reference is bound or the temporary that is
the complete object to a subobject of which the temporary is bound
persists for the lifetime of the reference or until the end of the
scope in which the temporary is created, whichever comes first.

Note the second condition of the or; the end of scope in which the
temporary is created is the end of the function. (I'm not surprised
that the committee defined it thus. The alternative would be an
implementation nightmare. And since the returned reference is a
temporary itself, its lifetime is only that of the complete calling
expression, so not having this restriction really wouldn't buy us much.)

Of course, all of this supposes a const reference; otherwise, according
to section 5.3.8, paragraphs 5-8, the initializer cannot be a temporary.

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

Andrew Gierth

unread,
Mar 5, 1997, 3:00:00 AM3/5/97
to

[narrowed to comp.std.c++]

>>>>> "Paul" == Paul Black <paul....@vf.vodafone.co.uk> writes:

> f...@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
>>
>> [snip] The function
>>
>> struct Foo { };
>> Foo & foo (Foo & x) { return Foo(x); }
>>
>> has undefined behaviour, because lifetime of the temporary object
>> Foo(x) ends at the end of the expression, so the function will
>> return a dangling reference.

Paul> If this is correct, then section 12.2, paragraph 5 of the draft
Paul> is wrong.

Are you referring to the following phrase:

[class.temporary]
5 ...
A temporary bound to the returned value in a function return
statement (_stmt.return_) persists until the function exits.

That implies that the temporary Foo(x) is destroyed on exit from foo().

--
Andrew.
---

James Kanze

unread,
Mar 5, 1997, 3:00:00 AM3/5/97
to

"Paul Black" <paul....@vf.vodafone.co.uk> writes:

|> f...@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
|> > [Note: this is crossposted to comp.lang.c++.moderated and comp.std.c++.
|> > If you follow up, please think about which group your reply belongs in.]


|> >
|> > [snip]
|> > The function
|> >
|> > struct Foo { };
|> > Foo & foo (Foo & x) {
|> > return Foo(x);
|> > }
|> >
|> > has undefined behaviour, because lifetime of the temporary object Foo(x)
|> > ends at the end of the expression, so the function will return a
|> > dangling
|> > reference.
|>

|> If this is correct, then section 12.2, paragraph 5 of the draft is
|> wrong.

Why? Fergus' wording is slightly sloppy, since in fact in this case
(according to the paragraph you cite), the temporary exists until the
end of scope in which it was created. In a return statement, however,
the end of scope pretty much corresponds to the end of full expression,
so there is no real difference.

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

Paul D. DeRocco

unread,
Mar 5, 1997, 3:00:00 AM3/5/97
to

Fergus Henderson wrote:

> As stated, this is not correct. The function


>
> struct Foo { };
> Foo & foo (Foo & x) {
> return Foo(x);
> }
>
> has undefined behaviour, because lifetime of the temporary object Foo(x)
> ends at the end of the expression, so the function will return a
> dangling
> reference.

Doesn't this expose a fundamental difference between a function return
and an exception throw? Aren't you allowed to do this with exceptions?

struct Foo {};

void throwfoo() {
Foo x;
throw x; // create temporary copy of x and throw it
}

void test() {
try {
throwfoo();
}
catch (Foo& z) {
// z should refer to the temporary, which still exists
}
}

As I read the exception handling part of the spec, some magic is
employed to allow the thrower of the exception to create a temporary
whose lifetime extends past the lifetime of the throw expression, all
thw way through the execution of the catch clause. How the heck to they
do that? Or am I misinformed?

--

Ciao,
Paul D. DeRocco

John E. Potter

unread,
Mar 5, 1997, 3:00:00 AM3/5/97
to

"Micha S. Berger" <ais...@IDT.NET> wrote:
> We all know you can't do
> int& foo() {
> int a = 3;
> return a;
> }

Actually, I think that you can (well formed); however, it invokes
undefined behavior.

> So, on a job interview I was asked about how to return a reference,
> without invoking new -- since new might lead to a memory link on the
> part of an unaware user of your class/library.

> Either way, after giving up (mumbling something about g++'s workaround),


> the interview claims that according to CD2,
> return Foo(x);
> will construct a Foo on the CALLER'S stack, not on the current
> function's.

I think that there is some lost communication here. Assume:

class Foo { Foo (int, int); }

and that the g++ _workaround_ mentioned is the named return value
extension which applies to values not references.

Foo bar (int x) return ret(x, x) { }

Some citations are:
3.10/6 A temp is an rvalue
8.5.3 A const& may be initialized by an rvalue, but a & requires an
lvalue.
12.8/15 Temporaries may be removed (return value)
12.2/5 Lifetime of temporaries.

I think that the example in 12.2/5 has a bug. The friend function should
return a C not a C const&. This may have led to the difference of
opinion which prompted the question.

Foo bar (int x) { return Foo(x, x); }
Foo bar (int x) { Foo ret(x, x); return ret; }

could both be optimized in the same way as the g++ return. I think it
has been announced that the return extension of g++ will be removed and
replaced with the permitted optimization. Note that it is permitted
not required; so, it is not a fact that the temporary will be removed
by all implementations. Any of them could be used to initialize a
const& in the caller which would give the effect of returning a
(const) reference without using new.

Foo const& likeNewFoo(bar(5));


Foo& bar (int x) { return Foo(x, x); }

This is ill-formed and requires a diagnostic (a warning is good enough).
A temporary rvalue may not be used to initialize a non-const reference.

Foo const& bar (int x) { return Foo(x, x); }

Back to the original, well-formed with undefined behavior since the
temp will die leaving a dangling const reference.

John
---

Herb Sutter

unread,
Mar 6, 1997, 3:00:00 AM3/6/97
to

"Paul D. DeRocco" <pder...@ix.netcom.com> wrote:
>Doesn't this expose a fundamental difference between a function return
>and an exception throw?
[snipped example showing catch-by-reference, which still performs a copy]

See Scott Meyers' discussion in More Effective C++, Item 12.

---
Herb Sutter (mailto:he...@cntc.com)

Current Network Technologies Corp.
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824
---


[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std...@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++...@ncar.ucar.edu ]

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]

Paul Black

unread,
Mar 6, 1997, 3:00:00 AM3/6/97
to

James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:
> "Paul Black" <paul....@vf.vodafone.co.uk> writes:
>
> |> f...@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
> |> > [snip]

> |> > The function
> |> >
> |> > struct Foo { };
> |> > Foo & foo (Foo & x) {
> |> > return Foo(x);
> |> > }
> |> >
> |> > has undefined behaviour, because the lifetime of the temporary object

> |> > Foo(x) ends at the end of the expression, so the function will
> |> > return a dangling reference.
> |>
> |> If this is correct, then section 12.2, paragraph 5 of the draft is
> |> wrong.
>
> Why? Fergus' wording is slightly sloppy, since in fact in this case
> (according to the paragraph you cite), the temporary exists until the
> end of scope in which it was created. In a return statement, however,
> the end of scope pretty much corresponds to the end of full expression,
> so there is no real difference.

I apologize, I should have said more (I had a cold so I was feeling lazy).
I was actually thinking of the example in paragraph 12.2/5. This example
states very clearly that the temporary result (which must be created in
"operator+(const C&, const C&)") is bound to the reference (cr) which is
external to the called function. As a reference to a temporary is
returned in the above example, I would expect that the return type
should be a "const Foo &".

Paul

Gerard Weatherby

unread,
Mar 6, 1997, 3:00:00 AM3/6/97
to

"Micha S. Berger" <ais...@IDT.NET> wrote:
> We all know you can't do
> int& foo() {
> int a = 3;
> return a;
> }

Am I reading a different draft standard than everyone else?

The following is from section 12.2.5.

"In all these cases, the temporaries created
during the evaluation of the expression initializing the reference,
EXCEPT the temporary to which the reference is bound, are destroyed at
the end of the full-expression in which they are created and in the
reverse order of the completion of their construction.
...

class C {
// ...
public:
C();
C(int);
friend const C& operator+(const C&, const C&);
~C();
};
C obj1;
const C& cr = C(16)+C(23);
C obj2;

the expression C(16)+C(23) creates three temporaries. A first tempo-
rary T1 to hold the result of the expression C(16), a second temporary
T2 to hold the result of the expression C(23), and a third temporary
T3 to hold the result of the addition of these two expressions.
...
The temporary T3 bound
to the reference cr is destroyed at the end of cr's lifetime, that is,
at the end of the program."

which says to me creating a temporary to satisfy a referenced is
well-formed: the temporary goes out of scope when the reference goes
out of scope.
---

James Kanze

unread,
Mar 6, 1997, 3:00:00 AM3/6/97
to

"Paul D. DeRocco" <pder...@ix.netcom.com> writes:

|> Fergus Henderson wrote:
|>
|> > As stated, this is not correct. The function


|> >
|> > struct Foo { };
|> > Foo & foo (Foo & x) {
|> > return Foo(x);
|> > }
|> >

|> > has undefined behaviour, because lifetime of the temporary object Foo(x)


|> > ends at the end of the expression, so the function will return a
|> > dangling
|> > reference.
|>

|> Doesn't this expose a fundamental difference between a function return

|> and an exception throw? Aren't you allowed to do this with exceptions?
|>
|> struct Foo {};
|>
|> void throwfoo() {
|> Foo x;
|> throw x; // create temporary copy of x and throw it
|> }
|>
|> void test() {
|> try {
|> throwfoo();
|> }
|> catch (Foo& z) {
|> // z should refer to the temporary, which still exists
|> }
|> }
|>
|> As I read the exception handling part of the spec, some magic is
|> employed to allow the thrower of the exception to create a temporary
|> whose lifetime extends past the lifetime of the throw expression, all
|> thw way through the execution of the catch clause. How the heck to they
|> do that? Or am I misinformed?

You're not misinformed. This is the case.

The only implementation I've familiar with is the one from Sun; they
reserve a separate buffer, and allocate the memory from that.

It also means that there are (generally unspoken) limits to nesting
exceptions; at one point or another, the run-time system will run out of
memory, and be unable to throw. I would suggest that if you run into
this, you're throwing far too many exceptions, except that it is hard to
say, not knowing what the limit is. (And of course, what ever it is,
you'll never run into it in your tests. Only the day of the big demo.)

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

---

Jason Merrill

unread,
Mar 7, 1997, 3:00:00 AM3/7/97
to

>>>>> Gerard Weatherby <ger...@alum.mit.edu> writes:

> "Micha S. Berger" <ais...@IDT.NET> wrote:
>> We all know you can't do
>> int& foo() {
>> int a = 3;
>> return a;
>> }

> Am I reading a different draft standard than everyone else?

No, just a different part of it.

> The following is from section 12.2.5.

> "In all these cases, the temporaries created
> during the evaluation of the expression initializing the reference,
> EXCEPT the temporary to which the reference is bound, are destroyed at
> the end of the full-expression in which they are created and in the
> reverse order of the completion of their construction.
> ...

But the beginning of that paragraph says

5 The second context is when a reference is bound to a temporary. The


temporary to which the reference is bound or the temporary that is the

complete object to a subobject of which the temporary is bound per-


sists for the lifetime of the reference or until the end of the scope
in which the temporary is created, whichever comes first.

Jason

James Kanze

unread,
Mar 7, 1997, 3:00:00 AM3/7/97
to

Gerard Weatherby <ger...@alum.mit.edu> writes:

|> "Micha S. Berger" <ais...@IDT.NET> wrote:
|> > We all know you can't do
|> > int& foo() {
|> > int a = 3;
|> > return a;
|> > }
|>
|> Am I reading a different draft standard than everyone else?
|>

|> The following is from section 12.2.5.
|>
|> "In all these cases, the temporaries created
|> during the evaluation of the expression initializing the reference,
|> EXCEPT the temporary to which the reference is bound, are destroyed at
|> the end of the full-expression in which they are created and in the
|> reverse order of the completion of their construction.
|> ...

I'm not sure where you're quoting this from. In my copy of the draft
(the Nov, 96 version, which I think is identical with the CD), section
12.2 has no subsections, and there is no text like this in paragraph 5
of 12.2.

The above text, quoted or not, is correct in as far as it goes.
Temporaries which are not bound to references are destroyed at the end
of the full expression. (There is actually one other exception.) This
doesn't say anything about the above, however. In my copy of the draft,
the applicable rule is that the temporary persists until the end of the
scope in which it was created. In a return statement, this is in
practice exactly the same as the end of the full expression.

|> class C {
|> // ...
|> public:
|> C();
|> C(int);
|> friend const C& operator+(const C&, const C&);
|> ~C();
|> };
|> C obj1;
|> const C& cr = C(16)+C(23);
|> C obj2;
|>
|> the expression C(16)+C(23) creates three temporaries. A first tempo-
|> rary T1 to hold the result of the expression C(16), a second temporary
|> T2 to hold the result of the expression C(23), and a third temporary
|> T3 to hold the result of the addition of these two expressions.
|> ...
|> The temporary T3 bound
|> to the reference cr is destroyed at the end of cr's lifetime, that is,
|> at the end of the program."
|>
|> which says to me creating a temporary to satisfy a referenced is
|> well-formed: the temporary goes out of scope when the reference goes
|> out of scope.

What you are saying is perfectly true in this case. To quote 12.2,
paragraph 5 for the n'th time: "The temporary to which the reference is
bound [...] persists for the lifetime of the reference or until the end


of the scope in which the temporary is created, whichever comes first."

The initial example is an obvious case where the "scope in which the
temporary is created" is shorter than the lifetime of the reference.
Function arguments are an obvious case of the reverse.

(If I understand the draft correctly, a temporary which is bound to a
reference parameter must be destroyed before the end of the full
expression. So, in an expression like "f(T()) + T()", the argument of
f() must be destructed before the other T(), regardless of the order of
construction. Of course, anyone who writes code which depends on such
subtilities deserves whatever compiler bugs he encounters, but this is
comp.STD.c++.)

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

Fergus Henderson

unread,
Mar 7, 1997, 3:00:00 AM3/7/97
to

"Paul D. DeRocco" <pder...@ix.netcom.com> writes:

>Fergus Henderson wrote:
>
>> As stated, this is not correct. The function
>>
>> struct Foo { };
>> Foo & foo (Foo & x) {
>> return Foo(x);
>> }
>>
>> has undefined behaviour, because lifetime of the temporary object Foo(x)
>> ends at the end of the expression, so the function will return a
>> dangling reference.
>
>Doesn't this expose a fundamental difference between a function return
>and an exception throw?

Uh, yes, I suppose you could look at it that way.

An exception throw is in many ways more like a function call than a
function return.

>Aren't you allowed to do this with exceptions?
>
> struct Foo {};
>
> void throwfoo() {
> Foo x;
> throw x; // create temporary copy of x and throw it
> }
>
> void test() {
> try {
> throwfoo();
> }
> catch (Foo& z) {
> // z should refer to the temporary, which still exists
> }
> }

That's correct.

>As I read the exception handling part of the spec, some magic is
>employed to allow the thrower of the exception to create a temporary
>whose lifetime extends past the lifetime of the throw expression, all
>thw way through the execution of the catch clause. How the heck to they
>do that?

Well, one way is to use two stacks, the ordinary call stack
and a separate stack for hold exception objects.
(The exception object stack may in fact be implemented as a linked
list of regions allocated on the heap.)

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]


[ about comp.lang.c++.moderated. First time posters: do this! ]

Aaron J Margosis

unread,
Mar 7, 1997, 3:00:00 AM3/7/97
to

There is a difference in this case, because the function foo in the
example is returning a reference, not an object. It could just as
easily be written:
Foo foo( Foo & x )
{
return Foo( x ) ;
}
which would return a temporary object, as opposed to a reference to a
temporary (which has gone out of scope).

Your exception example would throw an object, not a reference to one.
If you catch it with a reference, then you're simply binding that
reference to the temporary object.

Unless I'm misinformed.

Paul D. DeRocco wrote:
> Fergus Henderson wrote:
> > As stated, this is not correct. The function
> > struct Foo { };
> > Foo & foo (Foo & x) {
> > return Foo(x);
> > }
> > has undefined behaviour, because lifetime of the temporary object Foo(x)
> > ends at the end of the expression, so the function will return a
> > dangling
> > reference.
> Doesn't this expose a fundamental difference between a function return

> and an exception throw? Aren't you allowed to do this with exceptions?


> struct Foo {};
> void throwfoo() {
> Foo x;
> throw x; // create temporary copy of x and throw it
> }

[...]

-- Aaron

---------------------------------
Aaron J Margosis
Work e-mail: marg...@lccinc.com
Work phone: 703-516-6727
Personal: Aaron_M...@compuserve.com

John E. Potter

unread,
Mar 7, 1997, 3:00:00 AM3/7/97
to

Gerard Weatherby (ger...@alum.mit.edu) wrote:

: "Micha S. Berger" <ais...@IDT.NET> wrote:
: > We all know you can't do
: > int& foo() {
: > int a = 3;
: > return a;
: > }

: Am I reading a different draft standard than everyone else?

No, you are just seeing a different part which IMHO is a bug.

: The following is from section 12.2.5.

Most people write this 12.2/5


: class C {


: // ...
: public:
: C();
: C(int);
: friend const C& operator+(const C&, const C&);

Hello world ---------------^^^^^^^^

: ~C();


: };
: C obj1;
: const C& cr = C(16)+C(23);
: C obj2;

: the expression C(16)+C(23) creates three temporaries. A first tempo-
: rary T1 to hold the result of the expression C(16), a second temporary
: T2 to hold the result of the expression C(23), and a third temporary
: T3 to hold the result of the addition of these two expressions.
: ...
: The temporary T3 bound
: to the reference cr is destroyed at the end of cr's lifetime, that is,

: at the end of the program."

Since operator+ returned a const C&, where the hell is T3? It must
have been created inside of operator+. Thus the confusion.

Change the line above to

friend C operator+(const C&, const C&);

and T3 is the return value of the function which is bound to cr. Now
there is nothing to make people think that a temp inside of a function
lives past the end of the function. And they can read the words
everyone is quoting, not the buggy example.

John

Gerard Weatherby

unread,
Mar 8, 1997, 3:00:00 AM3/8/97
to

John E. Potter wrote:

> Since operator+ returned a const C&, where the hell is T3? It must
> have been created inside of operator+. Thus the confusion.

T3 is wherever the compiler vendor chooses to put it.


>
> Change the line above to
>
> friend C operator+(const C&, const C&);
>
> and T3 is the return value of the function which is bound to cr. Now
> there is nothing to make people think that a temp inside of a function
> lives past the end of the function. And they can read the words
> everyone is quoting, not the buggy example.
>
> John

It's more than just a buggy example. The text of 12.2/4 and 12.2/5
clearly
discusses circumstances upon which a temporary is destroyed at different
times than end of full expression.

"4 There are two contexts in which temporaries are destroyed at a
differ-
ent point than the end of the full-expression. The first context is
when an expression appears as an initializer for a declarator defining
an object. In that context, the temporary that holds the result of
the expression shall persist until the object's initialization is com-
plete. The object is initialized from a copy of the temporary; during
this copying, an implementation can call the copy constructor many
times; the temporary is destroyed after it has been copied, before or
when the initialization completes. If many temporaries are created by
the evaluation of the initializer, the temporaries are destroyed in


reverse order of the completion of their construction.

5 The second context is when a reference is bound to a temporary. The


temporary to which the reference is bound or the temporary that is the
complete object to a subobject of which the temporary is bound per-

sists for the lifetime of the reference or until the end of the scope

in which the temporary is created, whichever comes first. A temporary
holding the result of an initializer expression for a declarator that
declares a reference persists until the end of the scope in which the
reference declaration occurs. A temporary bound to a reference member

in a constructor's ctor-initializer (_class.base.init_) persists until
the constructor exits. A temporary bound to a reference parameter in
a function call (_expr.call_) persists until the completion of the
full expression containing the call. A temporary bound to the


returned value in a function return statement (_stmt.return_) persists

until the function exits. In all these cases, the temporaries created


during the evaluation of the expression initializing the reference,

except the temporary to which the reference is bound, are destroyed at


the end of the full-expression in which they are created and in the

reverse order of the completion of their construction. If the life-
time of two or more temporaries to which references are bound ends at
the same point, these temporaries are destroyed at that point in the
reverse order of the completion of their construction. In addition,
the destruction of temporaries bound to references shall take into
account the ordering of destruction of objects with static or auto-
matic storage duration (_basic.stc.static_, _basic.stc.auto_); that
is, if obj1 is an object with static or automatic storage duration
created before the temporary is created, the temporary shall be
destroyed before obj1 is destroyed; if obj2 is an object with static
or automatic storage duration created after the temporary is created,
the temporary shall be destroyed after obj2 is destroyed. [Example:


class C {
// ...
public:
C();
C(int);
friend const C& operator+(const C&, const C&);

~C();
};
C obj1;
const C& cr = C(16)+C(23);
C obj2;
the expression C(16)+C(23) creates three temporaries. A first tempo-
rary T1 to hold the result of the expression C(16), a second temporary
T2 to hold the result of the expression C(23), and a third temporary

T3 to hold the result of the addition of these two expressions. The
temporary T3 is then bound to the reference cr. It is unspecified
whether T1 or T2 is created first. On an implementation where T1 is
created before T2, it is guaranteed that T2 is destroyed before T1.
The temporaries T1 and T2 are bound to the reference parameters of
operator+; these temporaries are destroyed at the end of the full
expression containing the call to operator+. The temporary T3 bound


to the reference cr is destroyed at the end of cr's lifetime, that is,

at the end of the program. In addition, the order in which T3 is
destroyed takes into account the destruction order of other objects
with static storage duration. That is, because obj1 is constructed
before T3, and T3 is constructed before obj2, it is guaranteed that
obj2 is destroyed before T3, and that T3 is destroyed before obj1. ]"

While I don't think the prose is crystal-clear, when I read 12.2 as a
whole,
I then the temporary exists until the reference goes away.

Perhaps there is someone who was involved with working group discussions
on temporary who could shed some light on the intent on this section?

John E. Potter

unread,
Mar 9, 1997, 3:00:00 AM3/9/97
to

Gerard Weatherby <ger...@alum.mit.edu> wrote:
: John E. Potter wrote:

: > Since operator+ returned a const C&, where the hell is T3? It must
: > have been created inside of operator+. Thus the confusion.

: T3 is wherever the compiler vendor chooses to put it.

: It's more than just a buggy example. The text of 12.2/4 and 12.2/5


: clearly
: discusses circumstances upon which a temporary is destroyed at different
: times than end of full expression.

Yes, but I find nothing which says that a temporary constructed in a
function will be destroyed by the caller when it is returned by
reference.

Ok, let's assume that the class C has an int member named c, and write
the friend function. In a differenct translation unit, of course.

C const& operator+ (C const& lhs, C const& rhs) { // version 1
return C(lhs.c + rhs.c); // This is T3
}

C const& operator+ (C const& lhs, C const& rhs) { // version 2
static C ret;
ret = C(lhs.c + rhs.c); // This is T3
return ret;
}

C operator+ (C const& lhs, C const& rhs) { // version 3
return C(lhs.c + rhs.c); // return value optimize to return T3
}

: const C& cr = C(16)+C(23);

As James Kanze noted, the draft does not require the implementation to
distinguish between version 1 and 2. The temp is gone when the function
returns. In both of these cases, the initializer for cr is a C const&
not a temporary. With version 3, cr is initialized by the temporary
_value_ of the expression. It is always the responsibility of the
caller to destruct the value returned by a function.

FWIW: All four compilers available to me issued a diagnostic on
version 1. With version 3, no diagnostics. One lost both T1 and
T2, two lost T3, and the other destroyed all of the objects upon
termination (old rules).

: Perhaps there is someone who was involved with working group discussions


: on temporary who could shed some light on the intent on this section?

That would certainly be nice.

John

Gerard Weatherby

unread,
Mar 10, 1997, 3:00:00 AM3/10/97
to

James Kanze wrote:

> I'm not sure where you're quoting this from. In my copy of the draft
> (the Nov, 96 version, which I think is identical with the CD), section
> 12.2 has no subsections, and there is no text like this in paragraph 5
> of 12.2.


From the faq's
(http://reality.sgi.com/employees/austern_mti/std-c++/faq.html)
I followed
http: //www.setech.com/x3.html
and downloaded the December, 1996 version.

James Kanze

unread,
Mar 10, 1997, 3:00:00 AM3/10/97
to

Gerard Weatherby <ger...@alum.mit.edu> writes:

[Quoting the draft...]


|> 5 The second context is when a reference is bound to a temporary. The
|> temporary to which the reference is bound or the temporary that is the
|> complete object to a subobject of which the temporary is bound per-
|> sists for the lifetime of the reference or until the end of the scope

^^


|> in which the temporary is created, whichever comes first. A temporary

^^^^^^^^^^^^^^^^^^^^^


|> holding the result of an initializer expression for a declarator that
|> declares a reference persists until the end of the scope in which the

[...]


|> While I don't think the prose is crystal-clear, when I read 12.2 as a
|> whole,
|> I then the temporary exists until the reference goes away.
|>

|> Perhaps there is someone who was involved with working group discussions
|> on temporary who could shed some light on the intent on this section?

The problem is that the (non-normative) example has an error in it (I
think). I think that the underlined parts make it clear that returning a
reference to a temporary doesn't work; the temporary is created in the
scope of the *called* function.

I do have a question, however, concerning lifetimes that are shorter
than the end of full expression. If I read the standard correctly, when
a temporary is bound to a reference, its lifetime is no longer dependant
upon the full expression, even for a minimum. Thus, for example, a
temporary which is bound to a reference function argument must be
destructed immediately upon returning from the function, in virtue of
the above clause.

The intent of the "end of full expression" rule is, of course, to make
things like the following work:

extern void f( char const* ) ;
f( (s1 + s2).c_str() ) ;

The function string::c_str returns a pointer to internal memory of the
temporary, which will cease to be valid once the temporary is
destructed.

Now suppose a similar case, where the function c_str is not a member
function, but takes a string const&, e.g.:

extern void f( char const* ) ;
f( c_str( s1 + s2 ) ) ;

Is it really the intent that this should result in undefined behavior?
But if I read the draft correctly, since the temporary resulting from
the + operator is bound to a const reference, it must be destructed as
soon as the reference ceases to exist, i.e.: immediately upon return
from c_str, before calling f.

I don't think that this is what was intended, and I think that the
correction is really only editorial: 12.2/4 and 12.2/5 should explicitly
say that a temporary is NEVER destructed before the end of full
expression, even when it is ALSO governed by other rules. Thus, for
example, the section quoted above would become: "The temporary to which
the reference is bound [...] persists for the lifetime of the reference


or until the end of the scope in which the temporary is created,

whichever comes first, but at least until the end of the full expression
in which it is created." (This may, in fact, be enough in itself. I
think that in all other cases where other rules than end of full
expression applies, there is no way that they can result in less than
the full expression.)

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

James Kanze

unread,
Mar 11, 1997, 3:00:00 AM3/11/97
to

"Paul Black" <paul....@vf.vodafone.co.uk> writes:

|> James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:
|> > "Paul Black" <paul....@vf.vodafone.co.uk> writes:
|> >
|> > |> f...@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
|> > |> > [snip]

|> > |> > The function
|> > |> >
|> > |> > struct Foo { };
|> > |> > Foo & foo (Foo & x) {
|> > |> > return Foo(x);
|> > |> > }
|> > |> >

|> > |> > has undefined behaviour, because the lifetime of the temporary
|> > |> > object Foo(x) ends at the end of the expression, so the function


|> > |> > will return a dangling reference.
|> > |>

|> > |> If this is correct, then section 12.2, paragraph 5 of the draft is
|> > |> wrong.
|> >
|> > Why? Fergus' wording is slightly sloppy, since in fact in this case
|> > (according to the paragraph you cite), the temporary exists until the
|> > end of scope in which it was created. In a return statement, however,
|> > the end of scope pretty much corresponds to the end of full expression,
|> > so there is no real difference.
|>
|> I apologize, I should have said more (I had a cold so I was feeling lazy).
|> I was actually thinking of the example in paragraph 12.2/5. This example
|> states very clearly that the temporary result (which must be created in
|> "operator+(const C&, const C&)") is bound to the reference (cr) which is
|> external to the called function. As a reference to a temporary is
|> returned in the above example, I would expect that the return type
|> should be a "const Foo &".

The example contradicts the text, and examples are non normative. (I
think that it is just a typo in the example, and that the return value
should in fact be simply C, and not const C&.)

Brian Parker

unread,
Mar 11, 1997, 3:00:00 AM3/11/97
to

James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:

>I do have a question, however, concerning lifetimes that are shorter
>than the end of full expression. If I read the standard correctly, when
>a temporary is bound to a reference, its lifetime is no longer dependant
>upon the full expression, even for a minimum. Thus, for example, a
>temporary which is bound to a reference function argument must be
>destructed immediately upon returning from the function, in virtue of
>the above clause.

Sec 12.2 Clause 5 does have a line that says "A temporary bound to
reference parameter in a function call persists until the end of the


completion of the full expression containing the call."

Wouldn't this cover the case above?

Brian Parker (bpa...@gil.com.au)

James Kanze

unread,
Mar 12, 1997, 3:00:00 AM3/12/97
to

bpa...@gil.com.au (Brian Parker) writes:

|> James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:
|>
|> >I do have a question, however, concerning lifetimes that are shorter
|> >than the end of full expression. If I read the standard correctly, when
|> >a temporary is bound to a reference, its lifetime is no longer dependant
|> >upon the full expression, even for a minimum. Thus, for example, a
|> >temporary which is bound to a reference function argument must be
|> >destructed immediately upon returning from the function, in virtue of
|> >the above clause.
|>
|> Sec 12.2 Clause 5 does have a line that says "A temporary bound to
|> reference parameter in a function call persists until the end of the
|> completion of the full expression containing the call."
|> Wouldn't this cover the case above?

It would, but it is in direct contradiction with the second sentance in
the same paragraph.

--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --

John E. Potter

unread,
Mar 14, 1997, 3:00:00 AM3/14/97
to

James Kanze (james-alb...@vx.cit.alcatel.fr) wrote:
: bpa...@gil.com.au (Brian Parker) writes:

: |> James Kanze <james-alb...@vx.cit.alcatel.fr> wrote:
: |>
: |> >I do have a question, however, concerning lifetimes that are shorter
: |> >than the end of full expression. If I read the standard correctly,
: |> >when a temporary is bound to a reference, its lifetime is no longer
: |> >dependant
: |> >upon the full expression, even for a minimum. Thus, for example, a
: |> >temporary which is bound to a reference function argument must be
: |> >destructed immediately upon returning from the function, in virtue of
: |> >the above clause.
: |>
: |> Sec 12.2 Clause 5 does have a line that says "A temporary bound to
: |> reference parameter in a function call persists until the end of the
: |> completion of the full expression containing the call."
: |> Wouldn't this cover the case above?

: It would, but it is in direct contradiction with the second sentance in
: the same paragraph.

Can we make sence of this if we start with /3. It states that temporaries
are destroyed at the end of the full expression period. Then we proceed
to /4 which says, didn't really always mean that, there are exceptions.
So /4 covers covers temporaries as parameters to constructors. Then /5
covers the second case of binding to a reference. The second sentence
gives a generalization of the cases. Then specific cases are covered.
Next comes initializing a reference variable where scope of temp and
lifetime of reference are basically the same. Next is constructor
initializers for reference members which are extended to the exit of
the constructor. (Does this leave a dangling reference member?). Next
comes reference parameters to functions which are extended to the end
of the full expression containing them (recursively?). Next the
function return value (a reference which is about to dangle) is extended
to function exit. The rest covers order. So the specifics overrule
the generality. Does that work?

Still not convinced what is happening. Consider:
X const& noop (X const& x) { return x; }
void doSomething (X const& x) { ... }
doSomething(noop(X()));
Assuming that the above specific for parameters overrules the general
statement, the temporary might be destroyed following the call of noop
since that is the end of the full expression containing the call. It
would be easier if there were a concept of complete expression (;).
Isn't that some kind of a statement?

The other question which this brings up is a temporary with member
function invoked. In this case, the temporary is bound (by the
implementation) to a const pointer to non-const object not a reference
to const object. Is this lifetime covered elsewhere?

I started to answer your question and just got more. Reading standards
sure is hard, but not nearly as hard as writing them.

John

0 new messages