为什么返回不复制对象?

Why return doesn't copy the object?

本文关键字:对象 复制 返回 为什么      更新时间:2023-10-16

以下代码只打印A::A(),而不打印A::A(const A&)operator=。为什么?

struct A
{
A()               { cout << "A::A()" << endl; }
A(const A& value) { cout << "A::A(const A&)" << endl; }
A& operator=(const A& newValut)
{
cout << "A::operator=" << endl; 
return *this;
}
};
A foo()
{
A a;      //Ok, there we have to create local object by calling A::A().
return a; //And there we need to copy it, otherwise it will be destroyed
//because it's local object. But we don't.
}
int main()
{
A aa = foo(); //Also there we need to put result to the aa
//by calling A::A(const A&), but we don't.
}

所以这个代码必须打印

A::A()
A::A(const A&)
A::A(const A&)

但事实并非如此。为什么?

我建议在没有优化的情况下,g++下没有foo()的内联

这被称为"返回值优化"。在这种情况下,编译器可以删除副本。

这就是在C++中返回复杂类型的方式:返回对象的位置实际上是由调用方在调用函数之前提供的,指向这个尚未初始化对象的指针作为隐藏参数传递给函数。函数使用此内存位置从返回的表达式中构造返回的对象。

因此,当返回的对象要像程序A aa = foo();中那样直接初始化一个新对象时,它不需要将返回的值复制到堆栈上的对象。它要求函数直接在此位置创建对象。因此,最多只需要对复制构造函数进行一次调用。(事实上,如果对复制构造函数有两个调用,C++编译器将不兼容)。

现在,最重要的是,编译器可以在一个名为"返回值优化"或RVO的优化中优化掉这个调用。这怎么可能?如果您查看代码,您可以看到您可以在foo()中的返回值的建议位置直接定义本地"a"变量,因此不必再次复制它。这是一个重要的功能,因为复制构造函数可能很复杂,运行速度也很慢,所以在实现时可以显著提高性能(我认识的每个编译器都实现了这个功能)。

因此,在您的情况下,根据编译器的不同,您可能对复制构造函数有1或0调用,并且您的编译器仍然是兼容的。

在Visual Studio 2022 17.4及更高版本中,您可以使用/Zc:nrvo编译器选项显式启用可选的复制或移动省略行为。

https://learn.microsoft.com/en-us/cpp/build/reference/zc-nrvo?view=msvc-170