Topic: Timing of object destruction
Author: sdm@cs.brown.edu (Scott Meyers)
Date: 4 Aug 91 21:31:35 GMT Raw View
In the second edition of Lippman's C++ Primer, he discusses what happens
when the compiler sees this declaration:
X x = 10;
He talks about constructor calls, etc., and then he makes this statement,
on pages 503-4:
Finally, if X defines a destructor, the compiler must insert a call to
it AT THE POINT IMMEDIATELY FOLLOWING THE LAST USE OF x. [Emphasis
mine.]
This seems to be a good bit stronger than the ARM requirement that the
destructor for an object be called sometime between its last use and when
it goes out of scope.
The only thing I've read that might relate to this is in the June 1991 C++
Report, in which Dan Saks writes, under "Unresolved Core Language Issues,"
...the working group favored destroying temporaries as soon as
possible, but they weren't exactly sure what "as soon as possible"
is.
In the example above, however, notice that x isn't a temporary.
It's quite clear that Stan Lippman knows many things that I do not know.
However, is the timing of object destruction one of them?
Scott
-------------------------------------------------------------------------------
What do you say to a convicted felon in Providence? "Hello, Mr. Mayor."
Author: cok@acadia.Kodak.COM (David Cok)
Date: 5 Aug 91 12:23:45 GMT Raw View
In article <82557@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
|> In the second edition of Lippman's C++ Primer, he discusses what happens
|> when the compiler sees this declaration:
|>
|> X x = 10;
|>
|> He talks about constructor calls, etc., and then he makes this statement,
|> on pages 503-4:
|>
|> Finally, if X defines a destructor, the compiler must insert a call to
|> it AT THE POINT IMMEDIATELY FOLLOWING THE LAST USE OF x. [Emphasis
|> mine.]
|>
|> This seems to be a good bit stronger than the ARM requirement that the
|> destructor for an object be called sometime between its last use and when
|> it goes out of scope.
|>
|> The only thing I've read that might relate to this is in the June 1991 C++
|> Report, in which Dan Saks writes, under "Unresolved Core Language Issues,"
|>
|> ...the working group favored destroying temporaries as soon as
|> possible, but they weren't exactly sure what "as soon as possible"
|> is.
|>
|> In the example above, however, notice that x isn't a temporary.
|>
|> It's quite clear that Stan Lippman knows many things that I do not know.
|> However, is the timing of object destruction one of them?
|>
|> Scott
|>
The problem of when temporaries are destroyed was a significant one for us in
a project to develop a group of image classes. In this case the amount of memory
associated with an image was typically large and it was important that
temporaries be destroyed as soon as they are used. For example in the
expression A + B*C where A, B, and C are images (i.e. large arrays), a
temporary (T) must be formed to hold B*C. We needed that temporary to be
destroyed as soon as A+T was computed. The compiler we had (Sun C++ 2.0) did not
destroy such temporaries until the end of the block. This being unacceptable,
we had to explicitly keep track, via a private data member, of which images were
non-temporary and which were temporary and explicitly destroy the temporary ones
when they were inputs to an operation; this is an inelegant and error prone
technique.
I realize that in situations where there are references to internal parts of an
object, people would like temporaries to persist for a "while", but in the
situations described above, destruction as soon as the temporary is used in an
operation is the desirable rule. [ Someone else will have to post the real rule.]
David R. Cok
Eastman Kodak Company -- Imaging Science Lab
cok@Kodak.COM
Author: jamshid@ut-emx.uucp (Jamshid Afshar)
Date: 8 Aug 91 07:19:38 GMT Raw View
In article <82557@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:
>In the second edition of Lippman's C++ Primer, he discusses what happens
>when the compiler sees this declaration:
>
> X x = 10;
>
>He talks about constructor calls, etc., and then he makes this statement,
>on pages 503-4:
>
> Finally, if X defines a destructor, the compiler must insert a call to
> it AT THE POINT IMMEDIATELY FOLLOWING THE LAST USE OF x. [Emphasis
> mine.]
>
>This seems to be a good bit stronger than the ARM requirement that the
>destructor for an object be called sometime between its last use and when
>it goes out of scope.
Does the ARM allow a non-temporary object to be destroyed before it
goes out of scope (and after it is last used)? I hope not. For
example, an auto TextPrompt object at the beginning of a function
which displays a "Please wait... computing data." message.
The 2ndEd (which is my vote for the abbreviation of _The C++
Programming Language, 2nd Edition_) says in r.12.4 Destructors, p575
Destructors are invoked implicitly when an auto or
temporary object goes out of scope ...
and in r.12.2 Temporary Objects, p572
The compiler must ensure that a temporary object is destroyed. The
exact point of destruction is implementation dependent. There are
only two things that can be done with a temporary: fetch its value
(implicitly copying it) to use in some other expression, or bind a
reference to it. If the value of a temporary is fetched, that
temporary is then dead and can be destroyed immediately. If a
reference is bound to a temporary, the temporary must not be
destroyed until the reference is. ...
I believe this is the exact same wording as in the ARM. The 2ndEd
claims to describe the language as of May 1991, so if Lippman is
correct, the "immediately" requirement was introduced within the last
3 months (when was Lippman's published?).
Jamshid Afshar
jamshid@emx.utexas.edu
Author: chip@tct.com (Chip Salzenberg)
Date: 9 Aug 91 12:29:22 GMT Raw View
According to jamshid@ut-emx.uucp (Jamshid Afshar):
>The 2ndEd (which is my vote for the abbreviation of _The C++
>Programming Language, 2nd Edition_) ...
I suggest calling it "S2" ("S"=Stroustrup, by analogy with "K&R").
--
Chip Salzenberg at Teltronics/TCT <chip@tct.com>, <uunet!pdn!tct!chip>
If you meet Ken Thompson on the road, kill him.
Author: horstman@mathcs.sjsu.edu (Cay Horstmann)
Date: 10 Aug 91 05:32:17 GMT Raw View
In article <28A28AA2.5211@tct.com> chip@tct.com (Chip Salzenberg) writes:
>According to jamshid@ut-emx.uucp (Jamshid Afshar):
>>The 2ndEd (which is my vote for the abbreviation of _The C++
>>Programming Language, 2nd Edition_) ...
>
>I suggest calling it "S2" ("S"=Stroustrup, by analogy with "K&R").
>--
>Chip Salzenberg at Teltronics/TCT <chip@tct.com>, <uunet!pdn!tct!chip>
If you must be cute about it, call it S++.
Cay
Author: jimad@microsoft.UUCP (Jim ADCOCK)
Date: 5 Aug 91 21:01:46 GMT Raw View
In article <82557@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:
|In the second edition of Lippman's C++ Primer, he discusses what happens
|when the compiler sees this declaration:
|
| X x = 10;
|
|He talks about constructor calls, etc., and then he makes this statement,
|on pages 503-4:
|
| Finally, if X defines a destructor, the compiler must insert a call to
| it AT THE POINT IMMEDIATELY FOLLOWING THE LAST USE OF x. [Emphasis
| mine.]
|
|This seems to be a good bit stronger than the ARM requirement that the
|destructor for an object be called sometime between its last use and when
|it goes out of scope.
Hm, both these statements confuse me. ARM 6.7 states that destruction
of local variables is done at exit from their block. I always took this
as a precise statement of when named local variables are destructed.
This is different than unnamed temporaries. Presently the ANSI C++
committee is trying to decide what the bounds should be on when temps
can be destroyed:
| ...the working group favored destroying temporaries as soon as
| possible, but they weren't exactly sure what "as soon as possible"
| is.
The earliest a temporary could be destroyed is at the end of the
expression using it. The latest would be at the end of the block
the temporary was created in. Early destruction can conceivably lead
to better reuse of stack space, associate heap allocations, etc.
Late destruction is generally more convenient for programmers
[But, see Bjarne's arguments that late is never "late enough" for some
people [I agree, support GC :-]]
I guess today my preference would be that the scope of unnamed temporaries
be the "same" as named local variables -- thus based on "the principle
of least astonishment." Using this convention programmers can generally
either use "named" temporaries or "unnamed ones" or switch between the
two, without modifying the behavior of their code. In any case, this seems
to be what many C++ programmers expect. Consider "Trace" or "Lock" classes
for example. To get "correct" behavior under the assumption of
early destruction of temporaries requires that programmers introduce
dummy variable names in order to artificially "stretch" their lifetimes
to the end of the block.
Author: chip@tct.com (Chip Salzenberg)
Date: 12 Aug 91 14:27:05 GMT Raw View
According to horstman@mathcs.sjsu.edu (Cay Horstmann):
>In article <28A28AA2.5211@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>I suggest calling it "S2" ("S"=Stroustrup, by analogy with "K&R").
>
>If you must be cute about it, call it S++.
I wasn't trying to be cute. The original C Programming Language book
is called K&R; the second edition is called K&R2. It seems natural
enough to call Stroustrup's second book S2.
--
Chip Salzenberg at Teltronics/TCT <chip@tct.com>, <uunet!pdn!tct!chip>
If you meet Ken Thompson on the road, kill him.
Author: markr@cs.liv.ac.uk (Mark Rivers)
Date: 15 Aug 91 17:22:41 GMT Raw View
chip@tct.com (Chip Salzenberg) writes:
> According to horstman@mathcs.sjsu.edu (Cay Horstmann):
> >In article <28A28AA2.5211@tct.com> chip@tct.com (Chip Salzenberg) writes:
> >>I suggest calling it "S2" ("S"=Stroustrup, by analogy with "K&R").
> >
> >If you must be cute about it, call it S++.
>
> I wasn't trying to be cute. The original C Programming Language book
> is called K&R; the second edition is called K&R2. It seems natural
> enough to call Stroustrup's second book S2.
I never saw anyone refer to the first edition as "S" or "S1". If we are to
adopt "S2" perhaps we should refer to Lippman as "L" and Eckel as "E", too.
In the interests of standardisation we should stop referring to the ARM and
call it "E&S" instead.
I suggest that the new book is referred to as "CPL2", since "ARM" is a widely
used short form and that is taken from the book's title. If there is confusion
between CPL1 and K&R or CPL2 and K&R2 we could resolve this by appending the
ISBN number of the book we mean.
Mark :-)
Author: dl@g.g.oswego.edu (Doug Lea)
Date: 15 Aug 91 08:46:00 GMT Raw View
I can't help but re-state that the current temporary destruction rules
make some common constructs unsafe in ways that many programmers are
not aware. For example
#include <stream.h>
class X
{
int* a;
public:
X(int b) :a(new int(b)) {}
~X() { delete a; }
X& id() { return *this; }
int readA() { return *a; }
};
X f(int b) { return X(b); } // return an X as a temp
void print(int b) { cout << b; }
main()
{
print(f(1).id().readA());
}
The code in main() may fail since the temporary returned by f() may be
deleted after its use. But its only use here turns out to return
itself by reference. Thus the reference may be dangling by the time
that readA() and print() are invoked, causing trash to be printed (In
fact, running this through g++, which does the earliest possible temp
deletion allowed by the ARM resulted in `90488' to be printed when
this ran.)
In other words, since the use of a temporary in another expression
(i.e., a method or function call) may always legally propagate an
alias for it (in this case, via the anonymous return variable of
id()), it is not a great idea to delete it.
As Jim mentioned, the best solution is just(:-) to integrate GC in the
language instead.
But in any case, the current rules seem to invite subtle `bugs'. While
you could patch things up by inserting a rule that programmers may not
invoke methods or functions on temporaries that return or otherwise
bind their arguments or any parts thereof by reference or pointer,
enforcing such a rule would require the same (difficult) analysis that
you'd need in order to correctly postpone deletion anyway.
One initially attractive-looking compromise is to not delete a temp
used in a way that returns ANY ref or pointer until end of scope, but
to delete those used in value-returning expressions in the current
aggressive fashion. However, such a rule would still fail to deal
with the also-common case where the returned objects are proxies for
references. For example, SubString classes are necessarily defined to
contain a reference to the String of interest. When a substring
extraction method is applied to a temporary, the returned SubString
object can then have a dangling reference, causing failures like that
above. (I have received about a dozen `bug reports' for libg++ String
classes complaining about such cases (for which I offer no solution
other than telling people to give names to the anonymous return
variables, which magically causes them not to be deleted until the end
of the block) indicating that situations like this are not at all
rare.)
--
Doug Lea dl@g.oswego.edu || dl@cat.syr.edu || (315)341-2688 || (315)443-1060
|| Computer Science Department, SUNY Oswego, Oswego, NY 13126
|| Software Engineering Lab, NY CASE Center, Syracuse Univ., Syracuse NY 13244
Author: dag@control.lth.se (Dag Bruck)
Date: 16 Aug 91 06:35:39 GMT Raw View
I have the feeling that destroying temporaries at end of statement
would solve many problems.
- most problems seem to occur when a temporary is reused in a single
expression
- reusing space for every statement will prevent accumulation of space
for temporaries
- the rule is simple and intuitive (in my view)
- it probably doesn't solve every problem.
Comments anyone?
-- Dag
Author: lavender@mobius.aca.mcc.com (Greg Lavender)
Date: 16 Aug 91 16:37:50 GMT Raw View
> I have the feeling that destroying temporaries at end of statement
> would solve many problems.
> ...
> - the rule is simple and intuitive (in my view)
>
> Comments anyone?
>
> -- Dag
Exactly. You have my vote for a "textual scoping" rule for temporary
objects, i.e., temporaries should persist until the statement text
in which they occur is terminated. If one wants them to "live a
longer, fuller life", then copy them into a wider scope. We have file
scoping and block scoping, why not statement scoping?
Greg Lavender
----
MCC, 3500 W. Balcones Center Dr., Austin, Texas, USA 78759
Tel: 512.338.3252, Fax: 512.338.3600, E-Mail: lavender@mcc.com
--
-----
MCC, 3500 W. Balcones Center Dr., Austin, Texas, USA 78759
Tel: 512.338.3252, Fax: 512.338.3600, E-Mail: lavender@mcc.com
Author: ark@alice.att.com (Andrew Koenig)
Date: 16 Aug 91 13:34:40 GMT Raw View
In article <1991Aug16.063539.10295@lth.se> dag@control.lth.se (Dag Bruck) writes:
> I have the feeling that destroying temporaries at end of statement
> would solve many problems.
It would solve some problems and create others.
For example:
String s = /* something */;
String t = /* something else */;
Now, if this works:
printf ("%s\n", (char*) (s+t));
then I would expect this to work as well:
char* p = (char*) (s+t);
printf ("%s\n", p);
This shows that there are fairly intuitive cases where freeing
temporaries at the end of a statement isn't good enough. On the
other hand, delaying that long can be hard to implement:
printf ("%s\n", s.size() == 0 && t.size() == 0?
"(null)": (char*) (s+t));
After this statement, do we free the temporary for (s+t) or not?
Answer: only if it was created! Evidently, then, we must remember
whether or not it was created and do a run-time test when it's time to
free. If there are a lot of such temporaries, a test is potentially
needed for each one, even if most of them are never actually allocated.
> - most problems seem to occur when a temporary is reused in a single
> expression
Some people have complained about the complementary problem:
Matrix m1, m2, m3, m4;
Matrix m = m1 * m2 * m3 * m4;
Here it would be unfortunate if the implementation were prohibited
from deleting the three intermediate temporaries until after the
value for "m" is assigned.
> - the rule is simple and intuitive (in my view)
The rule is intuitive. Unfortunately, there are some common cases
in which its application is unintuitive, specifically when thinking
about when it is legal to break up a long statement into several
shorter ones.
--
--Andrew Koenig
ark@europa.att.com
Author: bmk@m2.csc.ti.com (Brian M Kennedy)
Date: 16 Aug 91 21:49:10 GMT Raw View
>=dag=>
>I have the feeling that destroying temporaries at end of statement
>would solve many problems.
>
>- most problems seem to occur when a temporary is reused in a single
>expression
>
>- reusing space for every statement will prevent accumulation of space
>for temporaries
>
>- the rule is simple and intuitive (in my view)
>
>- it probably doesn't solve every problem.
>
>Comments anyone?
=bmk=>
There is one complexity: conditional expressions. For instance:
matrix M7 = (((M1*M2 + M3 > M3*M4) || (M1+M4 < M5*M6) || (M3 > M6))
? (M1*M2 + M4 - M5)
: (M5 > M6 ? (M3*M4 - M1 - M2) : M6));
Do we want to require compilers to do the runtime bookkeeping to remember
what temporaries were constructed so that it knows what temporaries to
destruct at the end of the expression? I do not think so.
So, the next best rule is to destroy temporaries at the end of the outermost
non-conditional statement. This will still handle most problems and it will
prevent most temporary accumulation; however, it is no longer simple and
intuitive and there are even more problems that it will not solve.
Leaving it as is will leave a nice hole for people to get unknowingly burned.
The seemingly innocuous transformation of:
matrix M2 = M1.f();
matrix M3 = M2.g();
to:
matrix M3 = M1.f().g();
could transform valid code into erroneous code, without compiler warning.
And the error will be highly compiler-dependent. Scary.
I do not foresee a good language fix to this problem.
Thus, I currently *strongly* recommend that class writers *never* write
a public function that returns a pointer or reference to a part of its
representation. If you follow this rule, the problem ceases to exist.
Note that this eliminates certain C++ practices. For instance, traditional
smart pointers (via operator -> ()) are not possible; nor is assignment to
subscripted objects (M[3] = 6). Fortunately, there are good alternatives
to most of these practices.
== Brian M. Kennedy <bmk@csc.ti.com> ==
== Computer Systems Laboratory ========
== Computer Science Center ============
== Texas Instruments ==================
Author: Ari.Huttunen@hut.fi (Ari Juhani Huttunen)
Date: 18 Aug 91 12:41:51 GMT Raw View
In article <20754@alice.att.com> ark@alice.att.com (Andrew Koenig) writes:
>Some people have complained about the complementary problem:
> Matrix m1, m2, m3, m4;
> Matrix m = m1 * m2 * m3 * m4;
>Here it would be unfortunate if the implementation were prohibited
>from deleting the three intermediate temporaries until after the
>value for "m" is assigned.
In this case an additional rule would solve the problem. I leave it to you
to judge if it can be used with the other proposed rules.
m1 * m2 creates a temporary, let's call it t1
t1 * m3 creates temporary t2
Now, temporary t1 was used to create temporary t2. So delete t1. I.e.
whenever a temporary X is used to create temporary Y, then delete X.
t2 * m4 creates temporary t3
Delete t2
m = t3
Delete t3
--
...............................................................................
Ari Huttunen Ari.Huttunen@hut.fi I{-R'lyeh! Cthulhu fhtagn! I{! I{!
90-7285944
Author: euaeny@eua.ericsson.se (Erik Nyquist)
Date: 18 Aug 91 17:04:16 GMT Raw View
ark@alice.att.com (Andrew Koenig) writes:
>In article <1991Aug16.063539.10295@lth.se> dag@control.lth.se (Dag Bruck) writes:
>> I have the feeling that destroying temporaries at end of statement
>> would solve many problems.
>It would solve some problems and create others.
>For example:
> String s = /* something */;
> String t = /* something else */;
>Now, if this works:
> printf ("%s\n", (char*) (s+t));
>then I would expect this to work as well:
> char* p = (char*) (s+t);
> printf ("%s\n", p);
Hm...
ARM p268:
...
If a reference is bound to a temporary, the temporary must not be
destroyed until the reference is.
...
Is this another difference between pointers and references?
If I have understood the wording in ARM correctly the following
is legal and should work. (I have changed the char* -> const char*)
string& st = s+t;
printf ("%s\n", (const char*) st);
No such guarantee is given if the temporary has been bound to a pointer!
>This shows that there are fairly intuitive cases where freeing
>temporaries at the end of a statement isn't good enough. On the
>other hand, delaying that long can be hard to implement:
> printf ("%s\n", s.size() == 0 && t.size() == 0?
> "(null)": (char*) (s+t));
>After this statement, do we free the temporary for (s+t) or not?
>Answer: only if it was created! Evidently, then, we must remember
>whether or not it was created and do a run-time test when it's time to
>free. If there are a lot of such temporaries, a test is potentially
>needed for each one, even if most of them are never actually allocated.
I don't think such a test is too bad. This type of code looks ugly and
I can see no reason why it should be efficient.
if ( s.size() == 0 && t.size() == 0 )
printf("%s\n", "(null)");
else
printf("%s\n", (const char*)(s+t));
is easier to understand and doesn't give you this problem (???).
>> - most problems seem to occur when a temporary is reused in a single
>> expression
>Some people have complained about the complementary problem:
> Matrix m1, m2, m3, m4;
> Matrix m = m1 * m2 * m3 * m4;
>Here it would be unfortunate if the implementation were prohibited
>from deleting the three intermediate temporaries until after the
>value for "m" is assigned.
True, but could be rewritten as :
Matrix m1, m2, m3, m4;
Matrix m = m1 * m2; // One temporary created
tmp *= m3; // No temporary created
tmp *= m4; // No temporary created
by an experienced programmer to avoid this type of problem.
I suggested that the scope of temporaries should be even longer
than a simple statement, that they should be destroyed on exit from the
scope in which they where created. One reason for that was to avoid
the need for statement-scope.
Another reason was to be able to write this kind of code:
{
Lock("myfile.txt"); // Temporary file lock created
...
// File lock is released on exit from scope
// when the temporary's destructor is called.
}
Today we must give the temporary a name or bind a reference to it.
However I am not sure that this gives you much more in return and
perhaps it isn't a such good idea.
>> - the rule is simple and intuitive (in my view)
>The rule is intuitive. Unfortunately, there are some common cases
>in which its application is unintuitive, specifically when thinking
>about when it is legal to break up a long statement into several
>shorter ones.
>--
> --Andrew Koenig
> ark@europa.att.com
--
Erik Nyquist Ellemtel Utecklings AB We are no longer the knights that say Ni!
Box 1505 We are the knights that say:
S-125 25 Alvsjo, Sweden Iky,iky,iky,iky,patang,zoop-boing, zowie.
Author: jbn@lulea.telesoft.se (Johan Bengtsson)
Date: 19 Aug 91 11:51:27 GMT Raw View
euaeny@eua.ericsson.se (Erik Nyquist) writes:
< ark@alice.att.com (Andrew Koenig) writes:
<
< <In article <1991Aug16.063539.10295@lth.se> dag@control.lth.se (Dag Bruck) writes:
<
< << I have the feeling that destroying temporaries at end of statement
< << would solve many problems.
<
< <It would solve some problems and create others.
<
< < printf ("%s\n", s.size() == 0 && t.size() == 0?
< < "(null)": (char*) (s+t));
<
< <After this statement, do we free the temporary for (s+t) or not?
< <Answer: only if it was created! Evidently, then, we must remember
< <whether or not it was created and do a run-time test when it's time to
< <free. If there are a lot of such temporaries, a test is potentially
< <needed for each one, even if most of them are never actually allocated.
<
< I don't think such a test is too bad. This type of code looks ugly and
< I can see no reason why it should be efficient.
<
< if ( s.size() == 0 && t.size() == 0 )
< printf("%s\n", "(null)");
< else
< printf("%s\n", (const char*)(s+t));
<
< is easier to understand and doesn't give you this problem (???).
I think the ternary operator (?:) was mainly useful in C
to create nifty macros. In my view, inline functions make
this operator a true anachronism.
<
< << - most problems seem to occur when a temporary is reused in a single
< << expression
<
< <Some people have complained about the complementary problem:
<
< < Matrix m1, m2, m3, m4;
<
< < Matrix m = m1 * m2 * m3 * m4;
<
< <Here it would be unfortunate if the implementation were prohibited
< <from deleting the three intermediate temporaries until after the
< <value for "m" is assigned.
Maybe unfortunate, but I prefer a language that works
with a slightly non-optimal behaviour in some cases, rather
than a broken (but efficient!) one.
By the way, how large _are_ your matrices?
--
-----------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden |
| Email: jbn@lulea.telesoft.se; Voice: (+46) 92075471; Fax: (+46) 92075490 |
-----------------------------------------------------------------------------
Author: pat@bnrmtl.bnr.ca (Patrick Smith)
Date: 19 Aug 91 15:54:26 GMT Raw View
euaeny@eua.ericsson.se (Erik Nyquist) writes:
|> Hm...
|> ARM p268:
|> ...
|> If a reference is bound to a temporary, the temporary must not be
|> destroyed until the reference is.
|> ...
And the next sentence reads:
This destruction must take place before exit from the scope
in which the temporary is created.
Do these two statements contradict each other when the
reference in question is a class member? Then the
reference is destroyed when its containing object is
destroyed, but the second sentence imples that the temporary
must be destroyed before the constructor exits?
Is it even legal to bind a reference member of a class to
a temporary? It would be sensible (IMHO) to prohibit it.
Just for fun, I tried compiling this:
int f();
class X
{
int& Ref;
public:
X() : Ref(f()) {}
};
Both Oregon C++ 2.0.6 and Sun C++ (2.0, I think) accepted it.
--
Patrick Smith Bell-Northern Research, Montreal, Canada
(514) 765-7914 bnrmtl!pat@larry.mcrcim.mcgill.edu patrick@bnr.ca
... Any resemblance between the above views and those of my employer,
my terminal, or the view out my window are purely coincidental.
Author: pat@bnrmtl.bnr.ca (Patrick Smith)
Date: 19 Aug 91 16:04:43 GMT Raw View
euaeny@eua.ericsson.se (Erik Nyquist) writes:
|> ark@alice.att.com (Andrew Koenig) writes:
|>
|> > printf ("%s\n", s.size() == 0 && t.size() == 0?
|> > "(null)": (char*) (s+t));
|>
|> >After this statement, do we free the temporary for (s+t) or not?
|> >Answer: only if it was created! ...
|>
|> ... This type of code looks ugly and
|> I can see no reason why it should be efficient.
|>
|> if ( s.size() == 0 && t.size() == 0 )
|> printf("%s\n", "(null)");
|> else
|> printf("%s\n", (const char*)(s+t));
|>
|> is easier to understand and doesn't give you this problem (???).
Ugliness, like beauty, is in the eye of the beholder.
I am sure many people share your opinion. I am even more
confident that many people like the ?: operator and use
it extensively.
--
Patrick Smith Bell-Northern Research, Montreal, Canada
(514) 765-7914 bnrmtl!pat@larry.mcrcim.mcgill.edu patrick@bnr.ca
... Any resemblance between the above views and those of my employer,
my terminal, or the view out my window are purely coincidental.
Author: nevries@cs.ruu.nl (Nico de Vries)
Date: 19 Aug 91 14:20:50 GMT Raw View
I have an idea about object construction. I use it in my own
string library. Instead of just the type STRING I have and extra
type TSTRING (temporarely string). A string can be casted to a tstring.
This is done by making a copy of it. All operators and functions I wrote
take tstrings as arguments and return tstrings. The tstrings they get
as arguments are destroyed at the end of the function (by calling a special
function). The assignment operator converts a tstring into a string.
To assure efficience I allow multiple references to a single string. If one
reference changes a copy is made and it can start its own life.
With this method the line STRING a = "Hello" + b + "!" is executed properly.
A disadvantage is that in extra functions the tstrings have to be destroyed.
I am experimanting with an even more complicated approach where a+b+c etc
is kept as an expression tree untill an application of the result arives
to do it as efficient as possible (I hope).
Any opinions about this?
Nico de Vries
Author: jss@summit.lucid.com (Jerry Schwarz)
Date: 20 Aug 91 07:26:06 GMT Raw View
==ark=>
It would solve some problems and create others.
For example:
String s = /* something */;
String t = /* something else */;
Now, if this works:
[A] printf ("%s\n", (char*) (s+t));
then I would expect this to work as well:
char* p = (char*) (s+t);
[B] printf ("%s\n", p);
==jss=>
This kind of example cuts both ways. If the following works. (And
I've never heard anyone suggest that it shouldn't.)
string p = s+t;
[C] printf("%s\n", (char*)p);
then I would expect [A] above to work to work.
I think the only conclusion that can be drawn from this kind of
example is that "string::operator char*" is a dangerous operation that
will have to be used with care.
A programming discipline makes this easier. A policy that I think
makes sense is to not mix string and char* in one function and only
use the conversion operator when calling a function expecting a char*
from one using string. In that case [B] would immediately be
suspicious.
The use of printf in the above examples makes them atypical because it
is declared with ellipsis. Usually the conversion operator is
implicit. If
[D] FILE* f = fopen(s+t,"w") ;
is allowed (because of the declaration of the conversion operator),
but doesn't work (because of the early destruction of temporaries)
then I believe that implicit conversion should not be allowed, and
string ought to have an ordinary member to return a char*. The
consequences for interface design of having an ordinary member
function rather than a conversion operator are unpleasant although I
could probably live with them.
Author: steve@taumet.com (Stephen D. Clamage)
Date: 20 Aug 91 18:41:28 GMT Raw View
pat@bnrmtl.bnr.ca (Patrick Smith) writes:
|euaeny@eua.ericsson.se (Erik Nyquist) writes:
||> ARM p268:
||> If a reference is bound to a temporary, the temporary must not be
||> destroyed until the reference is.
|And the next sentence reads:
| This destruction must take place before exit from the scope
| in which the temporary is created.
|Do these two statements contradict each other when the
|reference in question is a class member? Then the
|reference is destroyed when its containing object is
|destroyed, but the second sentence imples that the temporary
|must be destroyed before the constructor exits?
|Is it even legal to bind a reference member of a class to
|a temporary? It would be sensible (IMHO) to prohibit it.
You must not bind a reference to an object which goes out of scope
before the reference does. It is not always possible for the compiler
to detect this. The classical example is
int & foo() { int i; return i; }
which some compilers detect.
|Just for fun, I tried compiling this:
| int f();
| class X
| {
| int& Ref;
| public:
| X() : Ref(f()) {}
| };
|Both Oregon C++ 2.0.6 and Sun C++ (2.0, I think) accepted it.
The compiler places the return from f() into a temporary and binds the
reference to that. The latest Oregon compiler (2.1.1) warns about it.
--
Steve Clamage, TauMetric Corp, steve@taumet.com