我制定了一个程序来评估这样做之间的性能差异:
func3(func2(func1()));
vs this:
retval1 = func1();
retval2 = func2(retval1);
func3(retval2);
我更喜欢后者的可读性和易于调试,我想知道编译器(MSVC 12.0)是否会优化发布版本中的中间对象。我的测试程序是这样的:
#include <iostream>
using namespace std;
struct Indicator {
Indicator() {cout << "Default constructor" << endl;}
Indicator(const Indicator& other) {cout << "Copy constructor" << endl;}
const Indicator& operator=(const Indicator& other) {cout << "Assignment operator" << endl;}
~Indicator() {cout << "Destructor" << endl;}
};
Indicator func1()
{return Indicator();}
Indicator func2(Indicator&& i)
{return std::move(i);}
Indicator func3(Indicator&& i)
{return std::move(i);}
int main() {
Indicator i = func3(func2(func1()));
cout << &i << endl;
return 0;
}
我很惊讶地发现,即使使用-O2,仍然会创建三个Indicator
实例:
Default constructor
Copy constructor
Copy constructor
Destructor
Destructor
00000000002EFC70
Destructor
Press <RETURN> to close this window...
这与我对移动语义的理解相冲突,即在这种情况下应该只创建一个Indicator
实例。我还认为编译器应该能够使用NRVO进行链式函数调用。有人可以向我解释这里发生了什么吗?
答案 0 :(得分:6)
在定义复制构造函数和复制赋值运算符时基于rule of 5,禁用了编译器生成的移动构造函数并移动赋值运算符。
如果你定义了移动构造函数,你将得到你期望的输出。
Indicator(Indicator&& other) {cout << "Move constructor" << endl;}
Indicator& operator=(Indicator&& other) {cout << "Move assignment operator" << endl;}