Non-Const Reference May Only Be Bound to an Lvalue

Non-const reference may only be bound to an lvalue

An lvalue is, roughly, whatever may be on the left side of an assignment statement. References provide aliases for other objects:

std::string s;
std::string & rs = s; // a non-const reference to s
std::string const & crs = s; // a const reference to s

Given the above definitions, referring to rs or crs is the same as referring to s, except that you cannot modify the referred string through crs, as it is const. A variable is an lvalue, so you are allowed to bind a non const reference to it. In contrast you can bind const references to temporary values as in:

std::string const & crs1 = std::string();

However the following is illegal:

std::string & rs1 = std::string();

This is because using non-const references you imply that you want to modify the referenced object. However temporaries bound to references are destroyed when the reference go out of scope. As it is not always intuitive when C++ creates temporary objects, binding them to non-const references has been disallowed, to avoid you the unpleasant surprise of changing your object as you like, just to see it destroyed a few statements later.

Non const lvalue references

That is because a temporary can not bind to a non-const reference.

double &m = a;

a is of type int and is being converted to double. So a temporary is created. Same is the case for user-defined types as well.

Foo &obj = Foo(); // You will see the same error message.

But in Visual Studio, it works fine because of a compiler extension enabled by default. But GCC will complain.

How come a non-const reference cannot bind to a temporary object?

From this Visual C++ blog article about rvalue references:

... C++ doesn't want you to accidentally
modify temporaries, but directly
calling a non-const member function on
a modifiable rvalue is explicit, so
it's allowed ...

Basically, you shouldn't try to modify temporaries for the very reason that they are temporary objects and will die any moment now. The reason you are allowed to call non-const methods is that, well, you are welcome to do some "stupid" things as long as you know what you are doing and you are explicit about it (like, using reinterpret_cast). But if you bind a temporary to a non-const reference, you can keep passing it around "forever" just to have your manipulation of the object disappear, because somewhere along the way you completely forgot this was a temporary.

If I were you, I would rethink the design of my functions. Why is g() accepting reference, does it modify the parameter? If no, make it const reference, if yes, why do you try to pass temporary to it, don't you care it's a temporary you are modifying? Why is getx() returning temporary anyway? If you share with us your real scenario and what you are trying to accomplish, you may get some good suggestions on how to do it.

Going against the language and fooling the compiler rarely solves problems - usually it creates problems.



Edit: Addressing questions in comment:
1) `X& x = getx().ref(); // OK when will x die?` - I don't know and I don't care, because this is exactly what I mean by "going against the language". The language says "temporaries die at the end of the statement, unless they are bound to const reference, in which case they die when the reference goes out of scope". Applying that rule, it seems x is already dead at the beginning of the next statement, since it's not bound to const reference (the compiler doesn't know what ref() returns). This is just a guess however.

  1. I stated the purpose clearly: you are not allowed to modify temporaries, because it just does not make sense (ignoring C++0x rvalue references). The question "then why am I allowed to call non-const members?" is a good one, but I don't have better answer than the one I already stated above.

  2. Well, if I'm right about x in X& x = getx().ref(); dying at the end of the statement, the problems are obvious.

Anyway, based on your question and comments I don't think even these extra answers will satisfy you. Here is a final attempt/summary: The C++ committee decided it doesn't make sense to modify temporaries, therefore, they disallowed binding to non-const references. May be some compiler implementation or historic issues were also involved, I don't know. Then, some specific case emerged, and it was decided that against all odds, they will still allow direct modification through calling non-const method. But that's an exception - you are generally not allowed to modify temporaries. Yes, C++ is often that weird.

One VS2010 bug ? Allowing binding non-const reference to rvalue WITHOUT EVEN a warning?

That is a known issue/feature of the VS compilers. They have always allowed that and there does not seem to be any push into removing that extension.

Why can a non-const reference parameter be bound to a temporary object?

If you compile with the /Za option to disable language extensions, the compiler rejects both calls:

> cl /Za test.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 18.00.21005.1 for x86
Copyright (C) Microsoft Corporation. All rights reserved.

test.cpp
test.cpp(11): error C2664: 'void f2(char &)' : cannot convert argument 1 from 'char' to 'char &'
test.cpp(12): error C2664: 'void f4(A &)' : cannot convert argument 1 from 'A' to 'A &'
A non-const reference may only be bound to an lvalue

There are several (very constrained) circumstances in which the compiler, with language extensions enabled, will still allow a non-const lvalue reference to bind to an rvalue expression. My understanding is that this is largely to avoid breaking several enormous legacy codebases that rely on this "extension."

(In general, use of /Za is not recommended for many reasons, but mostly because the Windows SDK headers cannot be #included with the /Za option.)

Why rvalue reference as return type can't be initialization of non-const reference?

I know that an rvalue reference is an lvalue.

You're talking about two different things: type and value category. e.g.

int&& ri = 0; // ri's type is rvalue reference (to int)
// ri's value category is lvalue; it's a named variable.

Given your 1st sample, what fun() returns is an xvalue, which belongs to rvalues.

The following expressions are xvalue expressions:

  • a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);

then,

int &a = fun(); // fails; lvalue-reference can't bind to rvalue

In the 2nd sample, what fun() returns is an lvalue,

The following expressions are lvalue expressions:

  • a function call or an overloaded operator expression, whose return type is lvalue reference, such as std::getline(std::cin, str),
    std::cout << 1, str1 = str2, or ++it;

then

int & a=fun(); // fine; lvalue-reference could bind to lvalue

In the 3rd sample,

decltype(fun()) b = 1; // the return type of fun() is rvalue-reference;
// this has nothing to do with the value category of its return value
// b's type is rvalue-reference too, btw its value category is lvalue

In the 4th sample,

int &&a = 1; // fine; rvalue-reference could bind to rvalue
// a's type is rvalue-reference, its value category is lvalue
int &b = a; // fine; lvalue-reference could bind to lvalue
// b's type is lvalue-reference, its value category is lvalue

Do *non*-const references prolong the lives of temporaries?

The behavior hasn't changed, you just need to turn your warning level up to /W4. VisualStudio implements the lifetime extension rule even for non-const lvalue references as a compiler extension. In this context, binding an rvalue to the non-const reference behaves the same as if you were binding it to a const reference.

With /W4 you'd see this:

warning C4239: nonstandard extension used : 'initializing' : conversion from 'Foo' to 'Foo &'
1> A non-const reference may only be bound to an lvalue

The text disallowing binding of an rvalue to a non-const lvalue reference can be found in §8.5.3/5

— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.
[ Example:

  double& rd2 = 2.0; // error: not an lvalue and reference not const
int i = 2;
double& rd3 = i; // error: type mismatch and reference not const

—end example ]

The second half of the quoted statement is what allows binding of a temporary to an rvalue reference, as shown in litb's answer.

string &&s = string("hello");

This, combined with the lifetime extension rule in §12.2/5, means the lifetime of the temporary will now match the lifetime of the (rvalue) reference it is bound to.



Related Topics



Leave a reply



Submit