临时绑定到引用参数的默认参数的生存期是多少



我认为引用只会将临时文件的生存期延长到引用本身的生存期,但以下片段的输出似乎是矛盾的:

#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()之后,临时性似乎被破坏了。。。什么东西?

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

p4在两种上下文中,临时性在与完整表达式结尾不同的点被销毁。[…]

p5第二个上下文是引用绑定到临时的。引用绑定到的临时对象或作为引用绑定到子对象的完整对象的临时对象在引用的生存期内持续存在,除外

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

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

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

有趣,+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!

因此,在调用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() ));

最新更新