基本上,我所做的如下。我的D类有三个构造函数(default、move、copy)和两个重载赋值操作符(move和copy)。我预计任何D类型对象的创建都会调用五个对象中的至少一个。
但是,按以下方式创建D对象“d4”不会调用其中任何一个对象:
D d4( foo() ); // foo returns a D
以下代码再现了我想知道的问题:
#include <iostream>
#include <vector>
#include <cassert>
using std::cout;
using std::endl;
class D {
public:
D()
{ cout << "D default"<<endl;}
D(const D& d)
{
cout << "D copy" << endl;
}
D(D&& d)
{
cout << "D rval" << endl;
assert(0);
}
D& operator=(D&& d)
{
cout << "D mv assign" << endl;
return *this;
}
D& operator=(const D& d)
{
cout << "D copy assign" << endl;
return *this;
}
volatile int v;
};
// return
D foo()
{
D res;
cout <<"returning a non const D" << endl;
return res;
}
int main()
{
D d4(foo());
return 0;
}
基本上,我假设将调用D(D&D)来创建d4,因为foo()返回一个可能不会获取其地址的临时对象。实际上,只有当-fno elide构造函数禁用了返回值优化时,才是这样。
但是,如果未指定,则默认情况下,RV优化即使在-O0上也会打开。那么,我看到的是:
D default
returning a non const D
我从stdout看到的都是foo()。d4本身的创建并没有给我任何帮助。这与我的预期不同。
我预期会出现以下情况。返回值的内存空间是在调用者的堆栈中分配的,而不是在被调用者的堆栈中分配的。调用默认构造函数来接触内存空间。不会从被调用方堆栈复制到调用方堆栈。然后,在调用方堆栈中分配另一个内存空间。由于返回的值是一个右值,因此调用move构造函数在“调用者堆栈中的另一个内存空间”上写入内容
我知道这可能需要冗余的内存空间。然而,特别是在我的示例中,move构造函数将与assert(0)一起消亡。但它会让程序继续。结果,返回值优化在程序输出方面产生了差异。
这是预期的吗?如果是,背后的原因是什么?我已经测试了g++-7.3.0和clang++-5.0.1。他们是一样的。