前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >rvo(copy_elision)总结

rvo(copy_elision)总结

原创
作者头像
江上摆渡翁
修改2020-07-07 16:50:50
9340
修改2020-07-07 16:50:50
举报
文章被收录于专栏:c++11

参考

RVO VS std :: move

(Named) Return Value Optimization

move constructor not called when using ternary expression in return statement?

概念

返回值优化(简称RVO)是一种编译器优化技术,它允许编译器在调用站点上构造函数的返回值。该技术也称为“清除”。C ++ 98/03标准不需要编译器提供RVO优化,但是大多数流行的C ++编译器都包含此优化技术

RVO使用

代码语言:txt
复制
#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

代码语言:txt
复制
//g++ xxx.cpp -std=c++11 -fno-elide-constructors
代码语言:txt
复制
constructor.        //localObj
代码语言:txt
复制
move constructor.   //tmp
代码语言:txt
复制
destructor.         //~localObj
代码语言:txt
复制
move constructor.   //obj
代码语言:txt
复制
destructor.         //~tmp
代码语言:txt
复制
destructor.         //~obj
代码语言:txt
复制
0x7ffff064f99f  constructor.            //localObj
代码语言:txt
复制
0x7ffff064f9bf  move constructor.       //tmp
代码语言:txt
复制
0x7ffff064f99f  destructor.             //~localObj
代码语言:txt
复制
0x7ffff064f9be  move constructor.       //obj
代码语言:txt
复制
0x7ffff064f9bf  destructor.             //~tmp
代码语言:txt
复制
0x7ffff064f9be  destructor.             //~obj
代码语言:txt
复制
////g++ xxx.cpp -std=c++11
代码语言:txt
复制
0x7fffb212e72f  constructor.    //localObj
代码语言:txt
复制
0x7fffb212e72f  destructor.     //~localObj

RVO原理

图片来自:

RVO VS std :: move

no rvo

7446a02d-31c5-48ec-8544-56411141f7d7.png
7446a02d-31c5-48ec-8544-56411141f7d7.png

rvo

5ce3d183-8424-4f1b-af08-cf2f0cfb3caa.png
5ce3d183-8424-4f1b-af08-cf2f0cfb3caa.png

结合图示和前面的demo代码发现,rvo优化的时候函数内部并没有新建临时对象,return的就是自动变量。并且后面赋值的时候,main函数中的对象也用的是foo函数中自动变量的地址。

代码语言:txt
复制
#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();
}
代码语言:txt
复制
0x7ffd4772908f  constructor. 
代码语言:txt
复制
0x7ffd4772908f
代码语言:txt
复制
0x7ffd4772908f
代码语言:txt
复制
0x7ffd4772908f  destructor.

RVO适用范围

引用自:(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):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
  • ...
  • when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move
  • ...

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构造函数也不能是私有。

总结

  • rvo可以减少对象拷贝,不调用构造函数生成临时对象,而是直接使用原来的对象,提升性能
  • 可以禁用rvo -fno-elide-constructor
  • 函数内的局部变量(必须直接返回同类型的变量名或匿名,不能是此函数或catch语句的参数,不能是条件表达式),可以更改变量直接构造在返回值里(临时对象)以节省一次复制/移动
  • 如果一个临时对象没有绑定在引用(左值或右值)上,这个临时对象可以直接构造在同类型的目标对象里(接收变量)以节省一次复制/移动
  • rvo是很早就出现的技术,copy elision是c++11后基于rvo提出的
  • 在满足rvo的条件下,会优先考虑move函数然后才是copy函数,这并不冲突,如果加了-fno-elide-constructor导致rvo失效,那么就能看到move函数被调用而不是copy函数。在rvo成功的情况下,这一原则不容易察觉到

测试

代码来自:(Named) Return Value Optimization

仅对结果进行分析:

代码语言:txt
复制
#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);
}
代码语言:txt
复制
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

myblog

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参考
  • 概念
  • RVO使用
  • RVO原理
  • RVO适用范围
  • 总结
  • 测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档