返回临时对象并绑定到常量引用

71
我的编译器对将临时变量赋值给常量引用没有任何抱怨:
string foo() {
  return string("123");
};

int main() {
  const string& val = foo();
  printf("%s\n", val.c_str());
  return 0;
}

为什么?我以为从foo返回的字符串是临时的,而val可以指向生命周期已经结束的对象。C++标准允许这样做并延长了返回对象的生命周期吗?

4
是的,只要是const引用,临时对象的生命周期就会延长。 - chris
4
这并不完全是2784262的副本,它(应该)处理引用为类成员的情况。行为是不同的。 - aberaud
2
@aberaud,这不仅不是重复的问题,而且是一个更好的问题,我很高兴我找到了它。 - dashesy
1个回答

91

这是C++的一个特性。代码有效且确实执行了它看起来要做的事情。

通常,临时对象仅持续到它出现的完整表达式的结束。然而,C++有意规定将临时对象绑定到堆栈上的const引用会延长临时对象的生命周期,使其与引用本身的生命周期相同,从而避免了常见的悬浮引用错误。在上面的例子中,由foo()返回的临时对象一直存活到闭合花括号。

P.S: 这仅适用于基于堆栈的引用。它不适用于作为对象成员的引用。

全文请参阅: GotW #88: A Candidate For the “Most Important const” by Herb Sutter.


2
另外,请注意,虽然这里没有对字符串进行复制,但是必须存在并且可以访问字符串的复制构造函数。 - xryl669
1
关于 P.S,这非常重要,上面链接的重复 SO 线程有很好的说明。 - underscore_d
@cxy669,“C++17保证复制省略”(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html)是否意味着不再需要复制构造函数?引用:“当源对象是临时对象时”。 - dashesy
@dashesy 你的意思是什么?在这种情况下,拷贝构造函数从来不是“必要”的。实际上,如果 f()return 表达式返回一个未命名的 _rvalue_,几乎肯定会触发 RVO 并省略拷贝构造,而是直接在调用者的接收位置(变量)中构造返回的对象。如果 return 表达式返回了一个已命名的对象,NRVO 可能也会这样做。C++17 的保证拷贝省略是在 RVO 更简单的情况下 要求 省略。 - underscore_d
@underscore_d 是的,没有复制,但是 xryl669 的评论指出仍然需要一个复制构造函数。 - dashesy
1
@dashesy 哦,我现在明白了。问得好。幸运的是,答案很容易找到:https://dev59.com/yFoT5IYBdhLWcg3w6isA#38043447 这是一个“是”。这从名称中的“保证”部分逻辑上可以推出。如果复制省略是有保证的,那么就不需要可访问的复制或移动构造函数,因为它保证永远不会被需要。 - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接