函数返回值与RVO优化

RVO优化:

return value optimistic,指当一个函数返回一个值类型而非引用类型时,可以绕过拷贝/移动构造函数,直接在调用函数的地方构造返回值。要发生rvo需要有三个条件:

1,如果类型是自定义类型,那么此时的拷贝/移动构造函数是可访问的

2,返回类型必须与实际类型完全一致,即返回时不需要进行隐式类型转换,否则不能进行rvo,因为不知道用户定义的类会不会有特别的类型转换构造函数

3,返回类型必须为值类型,而非引用类型,当然,这也是为什么需要rvo的原因--避免拷贝带来的额外开销

 

下面的代码是一个rvo的演示

复制代码
class Foo {
public:
    Foo() { cout << "default constructor  " << endl; }
    Foo(const Foo& f) { cout << "copy constructor  " << endl; }
    Foo(int i) { cout << "convert constructor   "  << endl; }
    Foo(Foo &&f) { cout << "move constructor  "  << endl; }
    ~Foo() { cout << "destructor " << endl; }
};

Foo fun(int i = 9) {
    Foo f1;
    return f1;
    //return std::move(f1);
}
int main(){
    Foo bob = fun();
    return 0;
}
复制代码

 

输出结果是这样的:

default constructor  
destructor 

 

在执行Foo bob = fun()时, 本意是主动呼叫拷贝构造函数,但是由于Foo定义了移动构造函数,使用普通的函数匹配规则来确定使用哪个构造函数,由于fun()的返回值是右值,所以使用移动构造函数,由于此时移动构造函数是可访问的,编译器可以绕过拷贝/移动构造函数,直接构造对象。所以不会先由fun()返回一个临时对象,再利用这个临时对象构造bob的过程,而是这个构造的临时量就是bob,不拷贝。

另外,函数的执行过程是,先执行函数体,在遇见return时,将return的值拷贝/移动到调用函数的地方,然后开始析构栈中的对象,但是rvo允许直接在调用函数的地方构造应该return的那个对象,所以这一次移动构造函数也被绕过了。

如果我们把这些优化都关掉,会得到这样的结果:

复制代码
default constructor  
move constructor 
destructor 
move constructor 
destructor 
destructor 
复制代码

即先构造f1,然后遇见return,移动到临时对象,析构f1,将临时对象移动到bob,析构临时对象,临时bob

 

但是如果我们这么写fun(),编译器就不会进行RVO:

复制代码
Foo fun(int i = 9) {
    Foo f1, f2;
    if (i < 0) {
        return f1;
    }
    return f2;
}
复制代码

前面说到,RVO是在构造需要return的对象时,直接在上一级栈帧中构造它(即调用函数的地方),而不是在它自己的栈帧中构造,这样就避免了将该对象拷贝至上级栈帧中的过程,可是修改后的fun,编译器无法在编译器推断出哪个对象会被返回,也就无从进行RVO,因此我们得到了这样的结果

复制代码
default constructor  
default constructor  
move constructor 
destructor 
destructor 
destructor 
复制代码

先构造出两个对象f1,f2,移动f2到函数调用处,析构f1,f2,这里只有一个移动构造函数是因为编译器优化掉了bob的移动构造函数。

 

总结:

1, 在构造对象时,如果有移动构造函数,使用普通函数匹配规则构造对象,否则正常构造。

2,正常构造函数时,使用'='就是主动呼叫移动构造函数即string s = "999"是让编译器先把"999"转换为string类型,再拷贝/移动,哪怕string类型有一个string(const char *)的构造函数,也要这么干

3,上面两个过程,在移动/拷贝构造函数都可以访问时,如果为移动构造函数,则直接拿着右值来使,不再调用移动构造函数,如果是拷贝构造函数,则直接拿着转换后的临时string来用,不拷贝,也就让string s = "999" 变成了string s("999") (很明显,隐式转换用的就是string(const char *)这个构造函数)

4, RVO优化只有同时满足:返回值是值类型,并且无需类型转换,编译器能够在编译器判断出哪个变量会被返回时,才会发生。发生RVO时,如果某个对象instance1会被返回,则不在该函数的栈帧中构造它,而是在调用函数的地方也就是上级栈帧中构造instance1。

5,函数在执行到return语句时,理论上先讲返回值拷贝/移动置调用的地方,然后开始析构函数栈中的自动对象

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值