从函数返回的对象的延长生命周期

Extended lifetime of an object returned from function

关于从函数 return 编辑并绑定到 rvalue/const 左值引用的对象生命周期的延长,我有一些不清楚的信息。来自 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.

如果我理解正确,引用声称对象的生命周期 returned by return statements 是不可延长的。但最后一句话表明,这仅适用于 returning 引用的函数。

在 GCC 上,此代码产生以下输出:

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

看来寿命延长了。
是否应该延长绑定到此类引用的临时对象的生命周期?另外能否提供更明确的信息来源?

引用自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.)

对于法律术语,请阅读 this SO answer

答案适用于局部常量引用和右值引用

So it looks like the lifetime got extended.

代码非常有效,但请注意,生命周期延长的对象不是Test{}在函数f()中创建的临时对象,它是函数[=]返回的对象13=]。返回的对象是从临时对象移动构造的,然后绑定到 t 并且生命周期得到延长。顺便说一句,返回的对象是按值返回的,它也是一个临时对象。

为了观察,您可以手动添加移动构造函数:

struct Test
{
  Test() { std::cout << "creation\n"; }
  ~Test() { std::cout << "destruction\n"; }
  Test(Test&&) { std::cout << "move\n"; }
};

并用禁止copy elision模式编译和运行,结果是:

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


引用标准,§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 ]