Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extended lifetime of an object returned from function

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?

like image 205
Criss Avatar asked Apr 11 '17 15:04

Criss


1 Answers

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

LIVE


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 ]

like image 91
songyuanyao Avatar answered Nov 09 '22 00:11

songyuanyao