Reputation: 611
There are some unclear information for me about extension of lifetime of an object returned from function and bound to rvalue/const lvalue reference. Information from here.
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.
If I understand it correctly, the quote claims that the lifetime of objects returned by return statements is not extendable. But the last sentence suggests, this only applies to functions returning references.
On GCC, this code produces the output below:
struct Test
{
Test() { std::cout << "creation\n"; }
~Test() { std::cout << "destruction\n"; }
};
Test f()
{
return Test{};
}
int main()
{
std::cout << "before f call\n";
Test && t = f();
std::cout << "after f call\n";
}
before f call
creation
after f call
destruction
So it looks like the lifetime got extended.
Should the lifetime of a temporary object bound to such reference be extended? Also could you provide any more clear source of informations?
Upvotes: 10
Views: 3446
Reputation: 172894
So it looks like the lifetime got extended.
The code is pretty valid, but note that the object whose lifetime got extended is not the temporary object created inside the function f()
by Test{}
, it's the returned object by the function f()
. That returned object is move-constructed from the temporary object, then gets bound to t
and lifetime gets extended. BTW the returned object is returned by value, and it's a temporary too.
For observation you can add move constructor manually:
struct Test
{
Test() { std::cout << "creation\n"; }
~Test() { std::cout << "destruction\n"; }
Test(Test&&) { std::cout << "move\n"; }
};
and compile and run with forbidding copy elision mode, the result is:
before f call
creation // the temporary created inside f
move // return object move-constructed
destruction // the temporary destroyed
after f call
destruction // the returned object destroyed
Quotes from the standard, §15.2/6 Temporary objects [class.temporary]:
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
(6.1) A temporary object bound to a reference parameter in a function call persists until the completion of the full-expression containing the call.
(6.2) The lifetime of a temporary bound to the returned value in a function return statement is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
(6.3) A temporary bound to a reference in a new-initializer persists until the completion of the full-expression containing the new-initializer. [ Example:
struct S { int mi; const std::pair<int,int>& mp; }; S a { 1, {2,3} }; S* p = new S{ 1, {2,3} }; // Creates dangling reference
— end example ] [ Note: This may introduce a dangling reference, and implementations are encouraged to issue a warning in such a case. — end note ]
Upvotes: 9
Reputation: 28659
Quoting from a GOTW article
A temporary object lasts only until the end of the full expression in which it appears. However, C++ deliberately specifies that binding a temporary object to a reference to const (or ravlue reference) on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.
string f() { return "abc"; }
void g() {
const string& s = f();
cout << s << endl; // can we still use the "temporary" object?
}
In the example above, the temporary returned by f() lives until the closing curly brace. (Note this only applies to stack-based references. It doesn’t work for references that are members of objects.)
For legalese, read this SO answer
The answer applies to both local const references and rvalue references
Upvotes: 1