10

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?

11
  • Yes it should, but I don't know were it is specified in the standard. Commented Apr 11, 2017 at 15:12
  • Test && t = f(); - that code makes no sense. Why do you declare object as rvalue? Commented Apr 11, 2017 at 15:13
  • @Criss sorry, removed comment, because I overlooked that && which may cause stuff that I am not familiar with. However, I dont really understand the question, as your f doesnt return a reference, but a copy of the local temporary, so everything should be fine Commented Apr 11, 2017 at 15:15
  • 1
    @jaskmar it's not an rvalue, it's an rvalue reference - those are 2 different things. rvalue is a value category, rvalue reference is a type Commented Apr 11, 2017 at 15:16
  • hm... maybe the lifetime of the returned temporary is supposed to end at the end of the line, but then still I dont see the relevance of the quote, as it is about returning a reference from a function. My guess would be that RVO is the reason you dont see the destruction of the temporary inside f Commented Apr 11, 2017 at 15:18

2 Answers 2

9

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 ]

Sign up to request clarification or add additional context in comments.

7 Comments

Ok, so lifetime of temporary (not exactly that created in f, I know) is getting extended, yes? Can you also point where standard says about these things?
@Criss Yes. And what the standard says is the same as cppreference.com; anyway I added to the answer.
Good answer - but please note that since C++17 copy elision is mandatory so no copy nor move constructor can be invoked. But the lifetime still will be extended.
Why after destruction of temporary object moved object not destructed also?
@Herrgott Which object are you referring to?
|
1

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

1 Comment

imho the presence of the function is a bit confusing (also in the question). Wouldnt it be exactly same situation and reasoning if it was const string& s = "abc"; ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.