initial value of reference to non-const must be an lvalue

Hi,

I have this simple code:

1
2
3
4
5
6
7
int g(double x) { return std::floor(x); }
int&& i(double x) { return g(x); }

void Do_Type_deduction()
{
	auto& li = i(8.8);
}     


However it does not compile, why? Does g(x) return a temporary object? If so, then its type must be int&& and yet the error list contains these two lines:


* initial value of reference to non-const must be an lvalue
* 'initializing': cannot convert from 'int' to 'int &'


in other words, shouldn't the error say "cannot convert from 'int&&' to 'int&'" ?

Regards,
Juan
Does g(x) return a temporary object?

Not exactly. g(some_double) is a prvalue expression. (A temporary object is produced when required.)

i(some_double) always returns a dangling reference. The dangling reference is bound to the temporary object materialized from g(x). The materialized temporary lives until the semicolon at the end of the return statement.

See:
https://en.cppreference.com/w/cpp/language/implicit_conversion#Temporary_materialization

Note that the problem occurs here: auto& li = i(8.8).
For the purposes of reference initialization the referenced object is considered instead of the reference.

Consider:
1
2
3
int g = 24;
int& x = g; // x refers to g
int& y = x; // y refers to g, not to x 


See:
https://en.cppreference.com/w/cpp/language/reference_initialization
Last edited on
> If so, then its type must be int&&
> shouldn't the error say "cannot convert from 'int&&' to 'int&'" ?

The value category of the expression i(8.8) is xvalue, and its type is int
(The type of an expression is never a reference type).
(The type of an expression is never a reference type).

Technically, you're right.

My understanding is that the standard adjusts the type of the expression from a reference type to a non-reference type to enable referential transparency when specifying the meaning of expressions.
https://timsong-cpp.github.io/cppwp/n4659/expr#5

Consider:
1
2
3
4
5
#include <type_traits>

int&& i(double);
static_assert(std::is_same<decltype(i(8.8)), int&&>::value,
              "i(8.8) has reference type");

http://coliru.stacked-crooked.com/a/b3c70b5f03773ebc
https://rextester.com/KUECRH45447

This doesn't disprove the claim, since it is actually decltype that adds the reference-ness back onto the type of the expression, depending on value category.
https://timsong-cpp.github.io/cppwp/n4659/dcl.type.simple#4

However, I'm not convinced that this rule is more than an implementation detail, and I'm not sure it should be taught. (The rule does explain the error message, at least.)

Edit. See what Scott Meyers has to say about it:
https://scottmeyers.blogspot.com/2015/02/expressions-can-have-reference-type.html

Edit 2.
Maybe it should be taught that references that appear in expressions are immediately replaced with their referent.
Last edited on
Actually, the error should be:


lvalue reference cannot bind to an xvalue


Shouldn't it?

See C++ Templates The Complete Guide Second Edition, page 679, authors Vandevoorde, Josuttis and Gregor
Last edited on
> Actually, the error should be: lvalue reference cannot bind to an xvalue Shouldn't it?

Well, these are pretty close.
I suppose it is simpler to emit the same error text for all rvalues (both xvalues and prvalues):
1
2
3
4
5
6
7
8
int&& foo() ;

int main()
{
    [[maybe_unused]] int& r = foo() ;
    // g++ - error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'
    // clang++ error = : non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
}

http://coliru.stacked-crooked.com/a/f46be9785219cffb
mbozzi wrote:
Edit. See what Scott Meyers has to say about it:

He's wrong. The type of an expression is never a reference type.
Topic archived. No new replies allowed.