Topic: Rationale behind disallowal of non-const r


Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/04/15
Raw View
In article <DAVEW.96Apr3110726@trigati.cs.haverford.edu>
davew@trigati.cs.haverford.edu (David G. Wonnacott) writes:

|> In article <4jevb1$kkp@engnews1.Eng.Sun.COM> clamage@Eng.sun.com (Steve Clamage) writes:

|>    From: clamage@Eng.sun.com (Steve Clamage)
|>    Newsgroups: comp.std.c++
|>    Date: 28 Mar 1996 21:16:23 GMT

|>    In article 96Mar27195129@trigati.cs.haverford.edu, davew@trigati.cs.haverford.edu (David G. Wonnacott) writes:
|>    >
|>    >I am looking for an explanation for the rationale behind the decision
|>    >of the standards committee to disallow the binding of a non-const
|>    >reference to an rvalue.

|>    The basic reason is that it usually is an error.
|>    ...

|>    Yes, there are times when you would like to be able to bind an rvalue
|>    to a non-const reference, since the only things you care about are
|>    sure to happen anyway.  ...        I believe the C++
|>    committee looked for ways to allow this binding of an rvalue to a non-const
|>    reference in cases where the results were what was intended, but failed to
|>    find a good way to define those cases.

|> What ever happened to "make it legal but recommend a warning?"  My
|> understanding of "illegal" is that the compiler should refuse to
|> compile the program.  I agree that the vast majority of occurances of
|> a non-const reference to an rvalue would be due to programmer errors.
|> But then again, so are many cases of "if (x=5) ...".

My understanding is that what happens in the case of "illegal" programs
is not defined by the standard.  For a certain category of errors, the
standard states that the compiler must issue a diagnostic; once that
occurs, you have undefined behavior.  (I think that we all would agree,
however, that it would be a pretty poor implementation that would go on
and do something bad having recognized an error.  But as some one once
pointed out to me in email: the `diagnostic' doesn't have to be on the
screen.  The implementor is free to document something to the effect
that `the diagnositic for errors requiring such in the standard is to
cause the light on the front of the hard disk to light up for 45
sec./Megabyte storage capacity.)

|> As I said, we make use of this all over the place, and its going to be
|> a real problem for us if this produces an error from most compilers.

|> We have a class for which (1) copying can be expensive, and (2) there
|> are some operations that extract information only after having a side
|> effect that can not be avoided (without copying).  Imagine if we could
|> do ++i but not i+1 for integers, and that copying them was expensive.
|> Suppose we we have a library that provides functions f1 and f2:

|>    int f1()         { ... }
|>    int f2(int &i)   { ++i; return i; }

|> (note that we copy the return values, but not the argument - it turns
|> out that we are more concerned with unnecessary copying for arguments
|> than return values).  The user of our library can then use y=f2(x),
|> and x will be incremented, and y will take on the new value.  We'd
|> like to let them use y=f2(f1()), to make y take on the value that is
|> one more than the value returned by f1().  The side-effect that was
|> performed on the object returned from f1 is lost, but we don't care.

|> I don't see any way of writing this without either (a) binding a
|> non-const reference to an rvalue, (b) forcing the user of our library
|> to set up an explicit temporary variable every time they use this
|> construct, or (c) giving up on avoiding this overhead and using call
|> by value for f2, or (d) doing something really brain-damaged like:

|>    int f2(const int &j) { int &i = (int &)j; i++; return i; }

|> But (a) is illegal, (b) is unreasonably inconvenient for our users,
|> (c) is inefficient, and (d) makes me queasy.

In the case of `int', I don't think there are any good choices.  But in
the case of `int', I don't think you need them.  For a user defined
class, I would certainly look into what could be done with mutable, and
declaring the parameters constant.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Ross Smith <alien@netlink.co.nz>
Date: 1996/04/03
Raw View
Steve Clamage wrote:
>
> Yes, there are times when you would like to be able to bind an rvalue
> to a non-const reference, since the only things you care about are
> sure to happen anyway. For example, given a class T:
>         T f1();     // f1 returns a T rvalue
>         f1().foo(); // ok only if foo is a const member function of T

I find this very surprising; perhaps I'm misunderstanding you. This seems
to disallow some very useful techniques.

As an example, suppose I have a string class with some member functions:

    class String {
      public:
        String Substring(int start, int length) const;
          // Extract a substring
        const String& LowerCase();
          // Convert to lower case, and return a const reference to *this
    };

Are you saying I can't write something like:

    String foo, bar;
    foo = bar.Substring(1, 10).LowerCase();

(i.e. the call to Substring() returns a temporary, which is then converted
to lower case, and copied to foo via String::operator= before the temp is
destroyed -- at least, that's what I thought would happen)

I use this sort of technique frequently, and neither of the compilers I use
(Symantec C++ 7.21 and IBM Visual Age C++ 3.0) raise any objection (IBM's
class library explicitly encourages this sort of thing, in fact).

Have I (and IBM and Symantec) got it wrong, or have I just misunderstood
what you wrote? If my code *is* legal, could you explain how it differs
from yours, which seems to be doing exactly the same thing?

--
Ross Smith ........................................ Wellington, New Zealand
Home: <mailto:alien@netlink.co.nz> ... Work: <mailto:ross.smith@nz.eds.com>
"I keep my ear very close to the ground, and in consequence I listen to a
lot of dog crap."                                           -- Alexei Sayle
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu                             ]





Author: davew@trigati.cs.haverford.edu (David G. Wonnacott)
Date: 1996/04/03
Raw View
In article <4jevb1$kkp@engnews1.Eng.Sun.COM> clamage@Eng.sun.com (Steve Clamage) writes:

   From: clamage@Eng.sun.com (Steve Clamage)
   Newsgroups: comp.std.c++
   Date: 28 Mar 1996 21:16:23 GMT

   In article 96Mar27195129@trigati.cs.haverford.edu, davew@trigati.cs.haverford.edu (David G. Wonnacott) writes:
   >
   >I am looking for an explanation for the rationale behind the decision
   >of the standards committee to disallow the binding of a non-const
   >reference to an rvalue.

   The basic reason is that it usually is an error.
   ...

   Yes, there are times when you would like to be able to bind an rvalue
   to a non-const reference, since the only things you care about are
   sure to happen anyway.  ...        I believe the C++
   committee looked for ways to allow this binding of an rvalue to a non-const
   reference in cases where the results were what was intended, but failed to
   find a good way to define those cases.

What ever happened to "make it legal but recommend a warning?"  My
understanding of "illegal" is that the compiler should refuse to
compile the program.  I agree that the vast majority of occurances of
a non-const reference to an rvalue would be due to programmer errors.
But then again, so are many cases of "if (x=5) ...".

As I said, we make use of this all over the place, and its going to be
a real problem for us if this produces an error from most compilers.

We have a class for which (1) copying can be expensive, and (2) there
are some operations that extract information only after having a side
effect that can not be avoided (without copying).  Imagine if we could
do ++i but not i+1 for integers, and that copying them was expensive.
Suppose we we have a library that provides functions f1 and f2:

   int f1()         { ... }
   int f2(int &i)   { ++i; return i; }

(note that we copy the return values, but not the argument - it turns
out that we are more concerned with unnecessary copying for arguments
than return values).  The user of our library can then use y=f2(x),
and x will be incremented, and y will take on the new value.  We'd
like to let them use y=f2(f1()), to make y take on the value that is
one more than the value returned by f1().  The side-effect that was
performed on the object returned from f1 is lost, but we don't care.

I don't see any way of writing this without either (a) binding a
non-const reference to an rvalue, (b) forcing the user of our library
to set up an explicit temporary variable every time they use this
construct, or (c) giving up on avoiding this overhead and using call
by value for f2, or (d) doing something really brain-damaged like:

   int f2(const int &j) { int &i = (int &)j; i++; return i; }

But (a) is illegal, (b) is unreasonably inconvenient for our users,
(c) is inefficient, and (d) makes me queasy.

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





Author: clamage@Eng.sun.com (Steve Clamage)
Date: 1996/03/28
Raw View
In article 96Mar27195129@trigati.cs.haverford.edu, davew@trigati.cs.haverford.edu (David G. Wonnacott) writes:
>
>I am looking for an explanation for the rationale behind the decision
>of the standards committee to disallow the binding of a non-const
>reference to an rvalue.

The basic reason is that it usually is an error. If you have a non-const,
(rather than a const) reference, the presumption is that you are likely to
modify the referenced object. Modifying an rvalue is not likely to be
what was intended. Two examples:

 double& rd = 2.0; // #1

In #1, we have a reference to ... what? "2.0" isn't an object, it is a
value. What should it mean to assign to rd? Why not write
 double rd = 2.0; // rd is now an object
instead? If you don't intend to assign to rd, make it a reference to const.
 const double& rd = 2.0; // OK
This case is rather minor. The main problem shows up in the next example.

 void f(double& rd);
 int k = ...;
 f(k); // #2

In #2, we should assume that function f is going to modify its actual
argument. But the actual argument has type int. An int can be converted
to a double, so the compiler could create a temp double, and pass a
reference to the temp. If f is declared to take a const reference, that is
what happens. That can't cause a problem, since the value can't be changed by
function f. But if f assigns to its non-const actual argument, the assumption
is that the actual argument is changed; that won't happen in this case. The
value of k is unaffected by anything that happens in f.

If you mean to allow passing an int to f, you have three choices: pass by
value instead of reference, or declare the formal parameter to be a reference
to a const double, or create an explicit variable of the right type and
initialize it with the value of k first:
 double t = k;
 f(t); // OK
Any of these approaches makes it clear that k is not going to be changed.

For simplicity, I have shown examples with int and double, which will
almost always have different sizes and representations. The arguments are
even stronger with class types. Please also note that when I say things like
"can't happen", I mean in a well-formed program.

Yes, there are times when you would like to be able to bind an rvalue
to a non-const reference, since the only things you care about are
sure to happen anyway. For example, given a class T:
 T f1();     // f1 returns a T rvalue
 f1().foo(); // ok only if foo is a const member function of T
Sometimes you don't care about the effects of foo on the object here,
and you only want the external side-effects of foo. I believe the C++
committee looked for ways to allow this binding of an rvalue to a non-const
reference in cases where the results were what was intended, but failed to
find a good way to define those cases.
---
Steve Clamage, stephen.clamage@eng.sun.com




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





Author: jarausch@igpm.rwth-aachen.de (Helmut Jarausch)
Date: 1996/03/29
Raw View
In article <4jevb1$kkp@engnews1.Eng.Sun.COM>, clamage@Eng.sun.com (Steve
Clamage) writes:
> In article 96Mar27195129@trigati.cs.haverford.edu,
> davew@trigati.cs.haverford.edu (David G. Wonnacott) writes:
> >
> >I am looking for an explanation for the rationale behind the decision
> >of the standards committee to disallow the binding of a non-const
> >reference to an rvalue.

SNIP



>
> Yes, there are times when you would like to be able to bind an rvalue
> to a non-const reference, since the only things you care about are
> sure to happen anyway. For example, given a class T:
>  T f1();     // f1 returns a T rvalue
>  f1().foo(); // ok only if foo is a const member function of T
> Sometimes you don't care about the effects of foo on the object here,
> and you only want the external side-effects of foo. I believe the C++
> committee looked for ways to allow this binding of an rvalue to a non-const
> reference in cases where the results were what was intended, but failed to
> find a good way to define those cases.

What about the following rule

class T
{
  change() { ...};
  lookat() { ...} const;
}

      T f1(){ ...};
const T f2(){...};

f1().change(); // should be OK
f2().change(); // should be an error
f2().lookat(); // should be OK

Helmut Jarausch
Institute of Technology
RWTH-Aachen
Germany
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]