返回值优化(RVO)场景下析构函数被调用两次的原因探究
关于Copy Elision(拷贝省略)的析构函数调用疑问
你的示例代码:
#include "iostream" using namespace std; struct Foo { Foo() { cout << "constructor was called" << endl; } ~Foo() { cout << "destructor was called" << endl; } }; Foo f() { Foo foo; return foo; } int main() { Foo bla = f(); return 0; }
程序输出:
constructor was called destructor was called destructor was called
问题解析
你看到两次析构的核心原因是编译器没有启用「命名返回值优化(NRVO)」——比如你用了Debug模式编译,默认会关闭大部分优化。
具体执行流程是:
- 在
f()函数内,foo对象正常构造(对应唯一一次构造输出)。 - 函数返回时,因为没有NRVO,编译器无法直接把
foo的内存空间复用给main里的bla,所以会先析构foo(第一次析构输出),同时通过默认拷贝构造生成一个返回临时对象(你没自定义拷贝构造,所以这一步无输出)。 - 到
main的Foo bla = f();语句时,编译器会执行「返回值优化(RVO)」,直接把这个临时对象构造在bla的内存空间里,因此临时对象不会额外触发析构。 - 最后
main函数结束,bla超出作用域被析构(第二次析构输出)。
如果开启编译优化(比如GCC/Clang加-O2参数,MSVC加/O2),NRVO会生效:f()里的foo会直接构造在bla的内存空间中,此时只会输出一次构造和一次析构,完全符合你对拷贝省略的预期。
另外补充:C++17之后部分拷贝省略是强制行为,但NRVO至今仍是编译器可选的优化项,是否生效取决于编译模式和编译器实现。
内容的提问来源于stack exchange,提问作者roi_saumon
相关产品推荐
相关产品推荐

