我们知道,对于一个函数的返回值来说,其是一个对象的拷贝。并且应当是一个右值。我们现在有一个函数
A get_A()
{
A a(1);
return a;
}
int mian()
{
A = get_A();
return 0;
}
这个函数的行为应当是在函数体中构造一个a,然后在返回的时候再复制一份a。
我们的本意是仅仅的得到一份a,但是却做了重复的拷贝工作。在编译器还不是那么智能的年代,这个问题不能得到很好的解决。但是现代C++中,编译器会为我们做优化。我们如上调用这个函数,编译器我们生成的目标代码就像是直接调用了A的构造函数一样。
struct A
{
int data;
A() : data(0)
{
cout << "construction" << endl;
}
A(int d) : data(d)
{
cout << "construction" << endl;
}
A(A && a) : data(a.data)
{
cout << "move" << endl;
}
A(const A & a) : data(a.data)
{
cout << "copy con" << endl;
}
~A()
{
cout << "destruction" << endl;
}
};
A get_A()
{
A a(1);
return a;
}
A get_A_p()
{
return A(1);
}
int main()
{
A a1 = get_A();
// A a2 = get_A_p()
cin.get();
return 0;
}
你可以拿着这么一个简短的代码去g++, 或者clang++,或者是任何一个支持返回值优化的编译上去运行,你将会得到如下的结果。
所以说,如果有编译器的优化,在些C++代码的时候关于返回值这里可以放心的去写。效率的工作编译器为我们承担了一部分。
不过为读者困惑的是,即便关闭了编译器优化,我么得到的目标文件还是可以进行返回值优化,如果想要关闭这种优化来看一下返回值具体的运作过程,需要使用以下的命令。
g++ main.cpp -o main -fno-elide-constructors
运行程序
这是一个惊人的结果!如果在对象比较大的时候还没有对应的移动构造,那么进行的无用复制可想而知。
来分析以下:
首先是函数内的A a(1) 对象,返回值用其移动构造,然后a自身销毁,返回值又移动构造给了main中的a1然后销毁。这个开销还是很大的。不过好在有编译器为我们优化,在现代C++设计中,有编译器的优化,我们可以不用在返回值的效率上杞人忧天了,岂不是十分舒服!
参考:
More Effective C++条款19,20
g++ 关闭返回值优化_天涯Kevin的博客-CSDN博客_g++ 关闭优化