Topic: Temporary objects question


Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1995/04/09
Raw View
jaf3@ritz.cec.wustl.edu (John Andrew Fingerhut) writes:

>I have compiled this code using two compilers: Sun 3.0 (cfront) and Sun 4.0.

As far as I can tell, both compilers are wrong, and so is g++ 2.6.3.

>Can someone please tell me what the current draft says about
>passing temporaries to functions that receive them as non-const and calling
>non-const member functions on temporaries.

According to the current draft, you can't bind a non-const reference to
a temporary, but you can call a non-const member function on a
temporary.

To be specific, 8.5.3 [dcl.init.ref] states that if the initializer is
not an lvalue, then the reference must be a const non-volatile reference,
and chapter 5 [expr] makes it clear that none of the initializers in your
examples are lvalues.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au





Author: fenster@ground.cs.columbia.edu (Sam Fenster)
Date: 1995/04/09
Raw View
fjh@munta.cs.mu.OZ.AU (Fergus Henderson) writes:
> According to the current draft, you can't bind a non-const reference to a
> temporary, but you can call a non-const member function on a temporary.

So I can't pass a temporary T to f(T&), but I can call T::f()?  Isn't this a
lack of orthogonality, since T::f takes an implicit `this' argument which
is "bound" to the temporary?

I'm glad the rules were changed so I can call T::f(), but why can't I call
f(T&) too?  The rationale for allowing it would be almost identical.

And as for
 extern T ft();
 const T &rt = ft();
Why does C++ allow this anyway?  Just to avoid calling a copy ctor?
 T t = ft();
is allowed to optimize the copy operation away, right?  rt and t would both
get destroyed at scope exit, right?  What's the advantage of using rt?

But as long as rt is allowed, why not allow it to be non-const?

I guess the rationale for disallowing modification of temps is that an
implicit type conversion might return one, and the programmer might think
she's modifying the original object.  And implicit conversions won't happen
when you call a member function, so that mistake can't happen then.

But it seems to me that there's no chance for this mistake to happen if a call
to f(T&) *doesn't* do implicit conversion of a temp, but *does* allow temps:
 void f (T&);
 void cf (const T&);
 struct S {operator T ();} s;

 cf (T());  // Has always been OK
 cf (s);    // Has always been OK:  Convert from S& to const T&
 // This would be error-prone:  User thinks she's modifying s:
 f (s);     // Rightfully illegal:  No conversion from S& to T&
 f (T());   // Wrongfully illegal:  Wouldn't be error-prone
 f ((T)s);  // Wrongfully illegal:  Wouldn't be error-prone

So the rules *should* say:
- A temp is not `const'.
- A non-const reference may always be bound to a temp.
- If a conversion from S to T is defined by a ctor or conversion operator,
then there is an implicit conversion from S to const T& but not from S to T&.

Thus, the restriction is placed on implicit conversions, where it belongs; not
on temps.

I believe this eliminates the possibility of the above-described programmer
error, while eliminating the undesirable restrictiveness of the current rules,
which make a temp const (except for member function calls).  Was there any
other rationale for disallowing binding a reference to a non-const temp?

I apologize in advance if my understanding is wrong.  This post is as much a
request for information as an argument for change.





Author: jaf3@ritz.cec.wustl.edu (John Andrew Fingerhut)
Date: 28 Mar 1995 16:25:58 -0600
Raw View
Recently, someone posted an article with the subject: "Mutable Temporaries".
I posted a follow-up suggesting where modifying a temporary would be useful,
that being where the modification of the temporary has a useful side effect.
The obvious example to me is to create a temporary ofstream or ostrstream
(or stringstream, although I still don't know what those look like) that
lives long enough to modify it.  The side effect is that a file receives
output or a char buffer gets modified in the process.

Someone responded via e-mail with some suggestions on how to accomplish the
same thing within the language definition.  I didn't feel that they had a
good understanding of the limitations of temporaries, and so I attempted
to explain things to them.  However, when I created my examples, I found
out that it was me that didn't have a good understanding!

Look at this example code:

/* 1 */  #include <iostream.h>
/* 2 */  #include <strstream.h>
/* 3 */  #include <fstream.h>
/* 4 */
/* 5 */  class Obj {
/* 6 */   int     i;
/* 7 */  public:
/* 8 */   Obj (int j) : i (j) { }
/* 9 */   friend ostream &operator<< (ostream &o, Obj &obj) {
/* 10 */          return o << obj.i;
/* 11 */  }
/* 12 */ };
/* 13 */
/* 14 */ ofstream func (char *s) { return ofstream (s); }
/* 15 */ void func1 (ostream &o, char *s) { o << s << endl; }
/* 16 */ void func2 (int &i) { cout << i << endl; }
/* 17 */ void func3 (Obj &o) { cout << o << endl; }
/* 18 */
/* 19 */ main ()
/* 20 */ {
/* 21 */  char    buf[256];
/* 22 */  ostrstream (buf, 256) << "This is a test" << ends;
/* 23 */  func ("file.dat") << buf << endl;
/* 24 */  func1 (func ("file.dat"), buf);
/* 25 */  func2 (int (3));
/* 26 */  func3 (Obj (3));
/* 27 */ }

I have compiled this code using two compilers: Sun 3.0 (cfront) and Sun 4.0.
Both compilers give warnings on line 25: "temporary passed to non-const
reference".  Sun 3.0 gives a warning on line 26 for the same reason, but
Sun 4.0 does not.  Neither compiler gives a warning or error line 24, even
if it is changed to read "func1 (ofstream ("file.dat"), buf);" (which I
considered to be equivilent in terms of temporaries).  In terms of temporaries,
I fail to see the difference in any one of the three lines (other than there
is obviously no side effect that can occur on the temporary int created for
line 25).  Can someone please tell me what the current draft says about
passing temporaries to functions that receive them as non-const and calling
non-const member functions on temporaries.

--
Stephen Gevers
sg3235@shelob.sbc.com