14

我认为引用只会将临时对象的生命周期延长到引用本身的生命周期,但以下代码段的输出似乎是矛盾的:

#include <iostream>

struct X{ ~X(){ std::cout << "Goodbye, cruel world!\n"; } };

X const& f(X const& x = X()){
  std::cout << "Inside f()\n";
  return x;
}

void g(X const& x){
  std::cout << "Inside g()\n";
}

int main(){
  g(f());
}

活生生的例子。输出:

Inside f()
Inside g()
Goodbye, cruel world!

所以看起来临时在g()被调用后被破坏了......什么给了?

4

3 回答 3

19

该标准在以下特殊情况下处理此问题§12.2 [class.temporary]

p4 有两种情况,其中临时对象在与完整表达式结尾不同的点被销毁。[...]

p5 第二个上下文是当一个引用绑定到一个临时的。引用绑定到的临时对象或作为引用绑定到的子对象的完整对象的临时对象在引用的生命周期内持续存在,但以下情况除外:

  • 临时绑定到函数调用 (5.2.2) 中的引用参数将持续存在,直到包含 call 的完整表达式完成

该标准还对完整表达式及其子表达式的评估与默认参数有一个方便的注释§1.9 [intro.execution] p11

[注意:完整表达式的评估可以包括对在词法上不属于完整表达式的子表达式的评估。例如,评估默认参数(8.3.6)所涉及的子表达式被认为是在调用函数的表达式中创建的,而不是在定义默认参数的表达式中创建的。——尾注]

于 2012-09-23T17:38:28.977 回答
1

有趣,+1。(我并不是要在这里与您的好自我回答竞争)。对任何感兴趣的人来说只是一个旁注。如果您想要类似的效果但允许非常量,您可以使用移动语义:

#include <iostream>

struct X{
   ~X(){ std::cout << "Goodbye, cruel world!\n"; }
   X(X && x){  std::cout << "moved "; }
   X(){}
};

X  f(X x = X()){
  std::cout << "Inside f()\n";
  return x;
}

void g(X x){
  std::cout << "Inside g()\n";
}

int main(){
   g(f());
}

Inside f()
moved Inside g()
Goodbye, cruel world!
Goodbye, cruel world!
于 2012-09-23T18:16:14.437 回答
0

所以看起来临时在 g() 被调用后被破坏了。

这是真的,因为临时的生命周期在 f() 被调用后开始,在 g() 退出后结束。那是因为:

在函数调用 (5.2.2) 中对引用参数的临时绑定一直存在,直到包含调用的完整表达式完成为止。

这里的完整表达式g(f())不是f()因为您的 f() 函数实际上看起来像这样

void g(X const& x){
  std::cout << "Inside g()\n";
}

X const& f(X const& x){
  std::cout << "Inside f()\n";
  return x;
}

g(f( X() ));
于 2022-02-19T06:07:34.470 回答