lvalue reference bind to a rvalue object

Hello,

Trying to understand the lvalue and rvalue references, and come up with some strange codes, which compile and run, but is confusing to understand.

Define this class Thing:

class Thing {
public:
Thing(int k = 0): i(k) {};
~Thing() { std::cout << "destroying Thing (i=" << i << ")" << std::endl; }
int getValue() const { return i; };
Thing &getMe() { return *this; };

private:
int i;
};

and a non-member function:

Thing construct_Thing10() {
Thing t(10);
return t;
}

Then these two lines in main():

Thing &thg=construct_Thing10().getMe();
std::cout << "member=" << thg.getValue() << std::endl;

The output is:

destroying Thing (i=10)
member=10

My understanding is that the rhs of line 1 construct only a temporary object. getMe() then return the reference of this temp object and bind it to thg (as a lvalue reference). After line one, the temp object is really destroyed (hence the first output line). At this point thg is really binding to a destroyed, invalid object. But somehow the 2nd line still prints the correct value of 10 is because the memory storage is not yet corrupted (still holding the previous value). Is this correct?
The second line print the correct value by pure luck. It just so happens that between destroying the object and printing the value it contained, that memory didn't get overwritten. It is undefined behavior - the program may crash, you may get a garbage value, or as you saw it may work.
Hi LB and Cubbi,

Thanks for the reply.

Your replies are more or less what I thought too. But things can get a bit more subtle here.

Note that in construct_Thing10(), the return value is Thing, not a pointer nor a reference. So in principle, a Thing object is constructed in construct_Thing10()'s stack, and then copied to another Thing object in the main()'s stack. thg is really bound to a temp object in main()'s stack. This is getting even more cryptic because, with more experiments, I didn't really see the copy constructor being executed, perhaps due to the return value optimization, whereby the copy construction is avoided by creating the local copy of Thing in the calling function's stack.

Now if I change the 1st line to:
Thing &&thg=construct_Thing10();

thg should still bind to the temp object on main()'s stack, but the output lines are reverse:
member=10
destroying Thing (i=10)

So the confusing part is that in both cases, thg was binding to a temp object on main()'s stack. But if thg is defined as Thing &, the temp object is destructed right after the assignment; whereas if thg is defined as Thing &&, the temp object is kept alive until the end of main().

Please feel free to add your opinions. Thanks.

Perhaps you need to review lifetime extension rules: http://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary

This expression creates and returns a temporary object: construct_Thing10()

This extends the temporary object's lifetme:
Thing &&thg=construct_Thing10();

This also extends the temporary object's lifetime:
const Thing &thg=construct_Thing10();

This expression returns a reference to the temporary object, which becomes a dangling reference once the expression finished: construct_Thing10().getMe(). Binding this reference to another reference does nothing for the object's lifetime.
Hi Cubbi,

So it looks like I am making the example more complicated by introducing the irrelevant function construct_Thing10(). The difference is just that

Thing &thg=Thing().getMe();

v.s.

Thing &&thg=Thing();

Thanks a lot for pointing me to the lifetime extension rules. That helps a lot to clear up my understanding. I will read it carefully.

Last edited on
Topic archived. No new replies allowed.