(Named) Return Value Optimization
move constructor not called when using ternary expression in return statement?
返回值优化(简称RVO)是一种编译器优化技术,它允许编译器在调用站点上构造函数的返回值。该技术也称为“清除”。C ++ 98/03标准不需要编译器提供RVO优化,但是大多数流行的C ++编译器都包含此优化技术
#include <iostream>
using namespace std;
class BigObject {
public:
BigObject() {
cout << this << " constructor. " << endl;
}
~BigObject() {
cout << this << " destructor."<< endl;
}
BigObject(const BigObject&) {
cout << this << " copy constructor." << endl;
}
BigObject(const BigObject&&) noexcept{
cout << this << " move constructor." << endl;
}
};
BigObject foo() {
BigObject localObj;
return localObj;
}
int main() {
BigObject obj = foo();
//foo();
}
output
//g++ xxx.cpp -std=c++11 -fno-elide-constructors
constructor. //localObj
move constructor. //tmp
destructor. //~localObj
move constructor. //obj
destructor. //~tmp
destructor. //~obj
0x7ffff064f99f constructor. //localObj
0x7ffff064f9bf move constructor. //tmp
0x7ffff064f99f destructor. //~localObj
0x7ffff064f9be move constructor. //obj
0x7ffff064f9bf destructor. //~tmp
0x7ffff064f9be destructor. //~obj
////g++ xxx.cpp -std=c++11
0x7fffb212e72f constructor. //localObj
0x7fffb212e72f destructor. //~localObj
图片来自:
no rvo
rvo
结合图示和前面的demo代码发现,rvo优化的时候函数内部并没有新建临时对象,return的就是自动变量
。并且后面赋值的时候,main函数中的对象也用的是foo函数中自动变量
的地址。
#include <iostream>
using namespace std;
class BigObject {
public:
BigObject() {
cout << this << " constructor. " << endl;
}
~BigObject() {
cout << this << " destructor."<< endl;
}
BigObject(const BigObject&) {
cout << this << " copy constructor." << endl;
}
BigObject(const BigObject&&) noexcept{
cout << this << " move constructor." << endl;
}
void print()
{
cout << this << endl;
}
};
BigObject foo() {
BigObject localObj;
localObj.print();
return localObj;
}
int main() {
BigObject obj = foo();
obj.print();
//foo();
}
0x7ffd4772908f constructor.
0x7ffd4772908f
0x7ffd4772908f
0x7ffd4772908f destructor.
引用自:(Named) Return Value Optimization
31.
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization. This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
1 return一个局部变量(必须直接返回同类型
的变量名或匿名,不能是此函数或catch语句的参数
,不能是条件表达式
),可以更改变量直接构造在返回值里(临时对象)以节省一次复制/移动。
2 如果一个临时对象没有绑定在引用(左值或右值)上,这个临时对象可以直接构造在同类型的目标对象里(接收变量)以节省一次复制/移动。
以上2点可以同时发挥作用(1 + 2),不生成临时对象以节省两次复制/移动。
32.
When the criteria for elision of a copy operation are met or would be met save for the fact that the source
object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to
select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload
resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to
the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an
lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will
occur. It determines the constructor to be called if elision is not performed, and the selected constructor
must be accessible even if the call is elided. -- end note ]
当满足条件31的时候,即使被copy的对象是左值,也会被优先当作右值来决定选择copy还是move构造函数(不管是否会优化而不被调用到)。简单地说:当满足条件31(放宽:加上函数参数是值传递的情况)的时候,隐式的move-on-return会被调用,否则fallback为copy。
即使可以省去copy/move构造函数的调用,copy/move构造函数也不能是私有。
代码来自:(Named) Return Value Optimization
仅对结果进行分析:
#include <iostream>
using namespace std;
class A {
public:
A() {
std::cout << "ctor" << std::endl;
}
~A() {
std::cout << "dtor" << std::endl;
}
A(const A&) {
std::cout << "cptor" << std::endl;
}
A(A&&) {
std::cout << "mvtor" << std::endl;
}
};
A g;
A f1() {
return A();
}
A f2() {
A a;
return a;
}
A f3(const A& a) {
return a;
}
A f4(A a) {
return a;
}
A f5() {
A a;
return std::move(a);
}
A&& f55() {
A a;
return std::move(a);
}
A f6(bool b = true) {
A a;
return b ? a : a;
}
A f66(bool b = true) {
A a;
A aa;
return b ? a : aa;
}
A f7(bool b = true) {
A a;
if (b) {
return a;
} else {
return a;
}
}
A f8() {
return g;
}
A& f9() {
A a;
return a;
}
A& f10(A a) {
return a;
}
A&& f11() {
A a;
return std::move(a);
}
A x = f1(); //rvo
ctor //g
ctor //unnamed
dtor
dtor
A x = f2(); //nrvo
ctor //g
ctor //a
dtor
dtor
const A& x = f2(); //同上
A&& x = f2(); //同上
A x = f3(g);
ctor //g
cptor //a->tmp
//a是函数参数,不符合31
//a是引用,非变量,不符合31,32
//x被赋值的时候使用了rvo
dtor
dtor
A x = f4(g);
ctor //g
cptor //a
//a是函数参数,不符合31
//a非return值,不符合32
mvtor //tmp a不符合31,但符合32
dtor
dtor
dtor
A x = f5();
ctor //g
ctor //a
mvtor //std::move后的右值-->tmp
dtor
dtor
dtor
A x = f55();
ctor //g
ctor //a
dtor //~a
mvtor //x
dtor
dtor
A x = f6(); //nrvo
ctor //g
ctor //a ? :的结果是确定的,符合rvo
dtor
dtor
A x = f66();
ctor //g
ctor //a
ctor //aa
cptor //return结果不确定,不符合31,32
//? : 的两个值都是左值,所以return左值。
dtor
dtor
dtor
dtor
A x = f7(); //rvo
A x = f8();
ctor //g
cptor //tmp
//x执行了rvo
dtor
dtor
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。