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

Rationale behind disallowal of non-const reference to rvalue

2 views
Skip to first unread message

David G. Wonnacott

unread,
Mar 28, 1996, 3:00:00 AM3/28/96
to
[Moderator's note: this article is crossposted to comp.std.c++ and
gnu.g++.help, and followups have been set to comp.std.c++. Please be
aware of what newsgroup your article is going to when you respond. mha]

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. I assume this has been discussed to death
here before, but I haven't been able to find it in a FAQ; I'd be happy
with a reference to another document rather than a full reply here.

The only answer I've seen for this question before is that it is
disallowed because the reference may outlive the rvalue it refers to.
But isn't this just as much a risk for const references as non-const?
I can't see why non-const references should be illegal when const
references are legal.

One other question (for the g++ group) - is the current plan for G++
to eventually give an error, rather than a warning, when this rule is
broken? If so, is there any possibility of a switch to disable this?

A detailed explanation of my question, and references, follow. Those
of you who have seen this before may want to skip the rest.

Paragraph 2 of Section 13.3.3.1.4 of the April draft of the C++
standard (http://www.cygnus.com/misc/wp/draft/, repeated below for
convenience) states that the following is illegal (at least as far as
I understand it):

class foo { ... };
foo f1();
int f2(foo &f);

void test()
{
int i = f2(f1());
}

g++ agrees with my reading, giving the following warning for this code:

In function `void test()':
warning: initialization of non-const `foo &' from rvalue `foo'
warning: in passing argument 1 of `f2(foo &)'

However, if f2 is changed to "foo f2(const foo &f);", there is no
problem (either according to my reading of the standard or to g++).

So - my questions are:

1) What can go wrong with f2 as it is written that can't go wrong with
f2 if it took a const foo &? I know that the reference could out-live
the compiler generated temporary - but that can happen with the
"legal" code as well.

2) If there is no error that would be prevented by the introduction of
const, why is the code legal with const and not without? Just because
nobody thought it would be useful to do this? We do it all over the
place in the Omega Library (http://www.cs.umd.edu/projects/omega) - we
want to perform an operation that extracts information from the
argument of f2, and return the extracted information. Whats the big
deal if this operation may have a side effect?

Puzzled,
Dave Wonnacott
da...@cs.haverford.edu


------- Relevant sections of http://www.cygnus.com/misc/wp/draft/ -------

13.3.3.1.4 Reference binding [over.ics.ref]

1 The operation of binding a reference is not a conversion, but for the
purposes of overload resolution it is considered to be part of a stan-
dard conversion sequence (specifically, it is the last step in such a
sequence).

2 A standard conversion sequence cannot be formed if it requires binding
a reference to non-const to an rvalue (except when binding an implicit
object parameter; see the special rules for that case in
_over.match.funcs_). [Note: this means, for example, that a candidate
function cannot be a viable function if it has a non-const reference
parameter (other than the implicit object parameter) and the corre-
sponding argument is a temporary or would require one to be created to
initialize the reference (see _dcl.init.ref_). ]
---
[ 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
]

Steve Clamage

unread,
Mar 28, 1996, 3:00:00 AM3/28/96
to
In article 96Mar2...@trigati.cs.haverford.edu, da...@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...@eng.sun.com


[ 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 ]

J. Kanze

unread,
Mar 29, 1996, 3:00:00 AM3/29/96
to
In article <DAVEW.96M...@trigati.cs.haverford.edu>

da...@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. I assume this has been discussed to death
|> here before, but I haven't been able to find it in a FAQ; I'd be happy
|> with a reference to another document rather than a full reply here.

First: it wasn't the committee's decision (except in so far as they
didn't change the base document). This was illegal in the ARM.

Consider the following:

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

unsigned j = 1 ;
incr( j ) ;

Without the rule, the above is perfectly legal code. However, the call
to `incr' does *NOT* modify `j', as the programmer probably expected it
would. The reason is that incr takes an int, not an unsigned. The
compiler implicitly converts, but the result of this conversion is not
an lvalue, but a temporary, and it is the temporary that gets bound to
the reference parameter.

I don't know when this rule was introduced, but it has been present in
all C++ texts I've seen except the first edition of Stroustrup (1986).
(Note that the exact example above appears on page 86 of _The Design_
_and_Evolution_of_C++. Given that this was originally allowed, and that
actual practical experience found it to be a mistake, it was hardly
likely that the committee would undo it.)

There have been suggestions that only implicit conversions should be
banned, and not all rvalues. However, as far as I know, no one ever
came up with a concrete proposal along these lines, and I believe that
the standard is too far advanced now to make the change (although it
sure would simplify the problems with auto_ptr:-).

|> The only answer I've seen for this question before is that it is
|> disallowed because the reference may outlive the rvalue it refers to.

If you want to know why certain features are (or are not) in C++, you
should definitly read the above mentioned book.
--
James Kanze (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung

Helmut Jarausch

unread,
Mar 29, 1996, 3:00:00 AM3/29/96
to
In article <4jevb1$k...@engnews1.Eng.Sun.COM>, cla...@Eng.sun.com (Steve
Clamage) writes:
> In article 96Mar2...@trigati.cs.haverford.edu,

> da...@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

David G. Wonnacott

unread,
Mar 30, 1996, 3:00:00 AM3/30/96
to
In article <KANZE.96M...@gabi.gabi-soft.fr> ka...@gabi-soft.fr (J. Kanze
) writes:

From: ka...@gabi-soft.fr (J. Kanze)
Newsgroups: comp.std.c++
Date: 29 Mar 1996 16:09:56 GMT

First: it wasn't the committee's decision (except in so far as they
didn't change the base document). This was illegal in the ARM.

I shouldn't have suggested the committee invented it.

The example you cite from p. 86 of the "Design & Evolution of C++"
does show a case in which the programmer doesn't get what they wanted.
I guess I'm just complaining because I'm used to the old approach of
"If it might be useful but is usually a mistake, make it a warning,
not an error". (For example, the ever popular "if (a = 5) ...").

The way the standard is written, I fear that this will become an
error, rather than a warning, in the near future. My impression is
that most compiler writers use the interpretation "if the standard
says its illegal, make it an error." I guess I'll go focus my
attention on the folks who are working on g++, in hope of ensuring
that at least g++ will continue to compile our code...

Dave W
---

Ross Smith

unread,
Apr 3, 1996, 3:00:00 AM4/3/96
to
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:al...@netlink.co.nz> ... Work: <mailto:ross....@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

David G. Wonnacott

unread,
Apr 3, 1996, 3:00:00 AM4/3/96
to
In article <4jevb1$k...@engnews1.Eng.Sun.COM> cla...@Eng.sun.com (Steve Clamage) writes:

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

In article 96Mar2...@trigati.cs.haverford.edu, da...@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
---

James Kanze US/ESC 60/3/141 #40763

unread,
Apr 15, 1996, 3:00:00 AM4/15/96
to std...@ncar.ucar.edu
In article <DAVEW.96A...@trigati.cs.haverford.edu>

da...@trigati.cs.haverford.edu (David G. Wonnacott) writes:

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

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

|> In article 96Mar2...@trigati.cs.haverford.edu, da...@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: ka...@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

0 new messages