Why rvalue reference binding to xvalue doesn't work in my code?
Asked Answered
T

1

14

I tried to understand lvalue and rvalue in C++11. So I wrote a test code:

int x = 10;
int foo() { return x; }
int& bar() { return x; }
int&& baz() { return 10; }

int main() {
    int& lr1 = 10;     // error: lvalue references rvalue
    int& lr2 = x;      // ok
    int& lr3 = foo();  // error: lvalue references rvalue
    int& lr4 = bar();  // ok
    int& lr5 = baz();  // error: lvalue references rvalue

    int&& rr1 = 10;    // ok
    int&& rr2 = x;     // error: rvalue references lvalue
    int&& rr3 = foo(); // ok
    int&& rr4 = bar(); // error: rvalue references lvalue
    int&& rr5 = baz(); // ok
}

It works pretty well, so I inserted std::cout to print results.

#include <iostream>

int x= 10;
int foo() { return x; }
int& bar() { return x; }
int&& baz() { return 10; }

int main() {
    int& lr1 = 10;     std::cout << lr1 << std::endl; // error
    int& lr2 = x;      std::cout << lr2 << std::endl; // ok
    int& lr3 = foo();  std::cout << lr3 << std::endl; // error
    int& lr4 = bar();  std::cout << lr4 << std::endl; // ok
    int& lr5 = baz();  std::cout << lr5 << std::endl; // error

    int&& rr1 = 10;    std::cout << rr1 << std::endl; // ok
    int&& rr2 = x;     std::cout << rr2 << std::endl; // error
    int&& rr3 = foo(); std::cout << rr3 << std::endl; // ok
    int&& rr4 = bar(); std::cout << rr4 << std::endl; // error
    int&& rr5 = baz(); std::cout << rr5 << std::endl; // ERROR!?
}

int&& rr5 = baz(); std::cout << rr5; causes a Runtime Error, but I don't know why it makes an error.

I think the return value of baz() would be xvalue, so its lifetime is prolonged. But when I tried to access its value, the error occurs. Why?

Twophase answered 11/7, 2017 at 9:48 Comment(8)
When asking questions about build errors, please include the actual error (in full, complete, unedited, copy-pasted as text) in the question body.Forsaken
Oh sorry. It makes a runtime error.Twophase
"xvalue so it's lifetime is prolonged" that would be a misconception. C++ prolongs lifetimes of temporaries, not lvalues, rvalues or prvalues. It does so when a reference is bound to such a temporary object. There is no temporary object in your expression. Moreover, bar and baz both invoke undefined behaviour, one by returning a reference to a local object and the other by failing to return anything.Johnajohnath
@n.m. x is not local.Sheer
@n.m. - bar returns a reference to a static at file scope.Sunflower
@StoryTeller sorry you are right of course. No UB in bar.Johnajohnath
Turn on your compiler warnings.Matelot
C++ prolongs the lifetime of temporary objects when bound to const references. No magic happens for mutable references.Sirloin
J
20

I think the return value of baz() would be xvalue, so its lifetime is prolonged.

At first what baz() returns is always a dangling reference.

For int&& baz() { return 10; }, the lifetime of the temporary is not extended. It's constructed inside the function and will be destroyed when get out of the function, then baz() always returns a dangling reference.

a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.

Then for int&& rr5 = baz();, rr5 is a dangling reference too; deference on it leads to UB and anything is possible.

On the other hand, if you change baz() to return-by-value, everything would be fine; the return value is copied and then bound to rr5, then the lifetime of the temporary is extended to the lifetime of rr5.

LIVE

Joellenjoelly answered 11/7, 2017 at 9:54 Comment(7)
Shouldn't it be saved in rr5 before the temporary is destroyed?Sheer
@Sheer It would have to be moved in the baz() function in order to do thatCattan
@Sheer - rr5 doesn't store anything. It's bound to an object. That object is essentially destroyed before the binding even happens.Sunflower
It means a function returning rvalue is only useful when it uses std::move() in returning?Twophase
Is int&& baz() { 10; } now equivalent to int&& baz() { return 10; }? I must have missed that improvement in C++...Johnajohnath
@n.m. I suppose it's a typo (which is fixed in the 2nd code snippet).Joellenjoelly
@Sheer As others said, the point is that the temporary is constructed inside the function, and destroyed when function returns, before being bound to rr5.Joellenjoelly

© 2022 - 2024 — McMap. All rights reserved.