专栏名称: CPP开发者
伯乐在线旗下账号,「CPP开发者」专注分享 C/C++ 开发相关的技术文章和工具资源。
目录
相关文章推荐
贵州省文化和旅游厅  ·  春天去哪儿?DeepSeek力荐贵州! ·  18 小时前  
贵州省文化和旅游厅  ·  春天去哪儿?DeepSeek力荐贵州! ·  18 小时前  
重庆市文化和旅游发展委员会  ·  今年春节泼天的流量,重庆接住了! ·  昨天  
重庆市文化和旅游发展委员会  ·  今年春节泼天的流量,重庆接住了! ·  昨天  
新疆是个好地方  ·  看《封神2》,这个5A级景区免门票! ·  2 天前  
新疆是个好地方  ·  看《封神2》,这个5A级景区免门票! ·  2 天前  
51好读  ›  专栏  ›  CPP开发者

性能大杀器:c++中的copy elision

CPP开发者  · 公众号  ·  · 2024-05-23 08:07

正文

在上一篇文章 性能大杀器:std::move 和 std::forward 中,我们简单的介绍了下移动语义,今天聊聊编译器的一个常见优化 拷贝消除(copy elision)

move和copy elision是一种常见的编译器优化技术,旨在避免不必要的临时对象的复制和拷贝,对于那种占用资源比较多的对象来说,这种优化无疑会很大程度上提升性能。

且看一个例子,如下:

#include 

struct Obj {
    

    Obj() {
       std::cout <"Default ctor" <    }

    Obj(const Obj& r) {
        std::cout <"Copy ctor" <    }
   
    int x_ = 0;
};

Obj CreateObj1() {
    return Obj();
}

Obj CreateObj2() {
    Obj temp;
    temp.x_ = 42;
    return temp;
}

int main() {
    Obj o1(CreateObj1());
    Obj o2(CreateObj2());
   
    return 0;
}

编译并运行上述代码,输出:

Default ctor
Default ctor

PS:本文中所使用的编译器及版本为 gcc 11.4.0 ,如果未做显式说明,在编译过程中都加上 -std=c++11 选项。

好了,仍然是上面的代码,如果编译选项变成 -std=c++11 -fno-elide-constructors ,输出试试,看看会是什么结果~~

emm,在本地尝试编译并运行了下:

Default ctor
Copy ctor
Copy ctor
Default ctor
Copy ctor
Copy ctor

与最开始的输出相比,多了很多,现在我们着手分析下原因,以 Obj o1(CreateObj1()); 为例:

调用CreateObj1()函数,创建一个临时对象并返回,此时会输出 Default ctor 将上述的需要返回的临时对象以拷贝方式赋值给函数返回值,此时会输出 Copy ctor 函数返回值作为obj1的拷贝对象,此时会输出 Copy ctor

接着分析下 Obj o2(CreateObj2()); :

CreateObj2()创建一个临时变量temp,此时会输出 Default ctor 修改临时变量temp的成员变量x_的值为2 temp以拷贝方式赋值给函数返回值,此时会输出 Copy ctor 函数返回值作为obj2的拷贝对象,此时会输出 Copy ctor

对前面的输出做个简单总结,如下:

Default ctor // 在CreateObj1中以Obj()方式创建临时变量T1
Copy ctor    // T1以复制拷贝的方式赋值给CreateObj1()函数返回值,此处假设为T2
Copy ctor    // 通过调用拷贝构造函数,将T2值赋值给o1
Default ctor // 创建临时变量temp
Copy ctor    // temp以复制拷贝的方式赋值给CreateObj1()函数返回值,此处假设为temp2
Copy ctor    // 通过调用拷贝构造函数,将temp2值赋值给o2

在上一节中,我们提到过,可以通过使用移动构造的方式来避免拷贝,为了测试该功能,尝试在Obj类中新增一个移动构造函数:

#include 

struct Obj {
    Obj() {
       std::cout <"Default ctor" <    }

    Obj(const Obj& r) {
        std::cout <"Copy ctor" <    }
    
    Obj(const Obj&& r) { // 移动构造函数
        std::cout <"Move ctor" <    }
    
    int x_ = 0;
};

Obj CreateObj1() {
    return Obj();
}

Obj CreateObj2() {
    Obj temp;
    temp.x_ = 42;
    return temp;
}

int main() {
    Obj o1(CreateObj1());
    Obj o2(CreateObj2());
   
    return 0;
}

输出如下(编译选项为 -std=c++11 -fno-elide-constructors ):

Default ctor
Move ctor
Move ctor
Default ctor
Move ctor
Move ctor

看了上述输出,不禁奇怪,为什么在 CreateObj2()函数中,创建的temp明明是一个左值,此处却调用的是移动构造即当做右值使用呢? ,我们不妨看看标准对此处的解释:

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 parameter or a variable introduced by the exception-declaration of a handler (14.4)) with the same type (ignoring cv-qualification) as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function call’s return object

意思是当返回语句中的表达式是一个非volatile的命名对象,其类型与函数的返回类型相同时,编译器可以优化掉拷贝或移动操作,直接将自动对象构造到函数调用的返回对象中。

这意味着,当函数返回一个自动对象时,编译器可以优化掉不必要的拷贝或移动操作,直接将自动对象构造到函数调用的返回对象中,以提高效率。这种优化在 C++ 标准中被明确规定,以支持更高效的代码生成。

标准的这一规定,使得原本不支持拷贝的对象,作为函数返回值时,也成了可能。

众所周知, std::unique_ptr<> 不支持拷贝操作,即:

std::unique_ptr<int> p1 = std::make_unique<int>(1);
std::unique_ptr<int> p2 = p1;

上述代码将编译失败,错误提示如下:

error: use of deleted function 'std::unique_ptr<_tp>::unique_ptr(const std::unique_ptr<_tp>&) [with _Tp = int; _Dp = std::default_delete]'
    std::unique_ptr<int> p2 = p1;
note: declared here
  unique_ptr(const unique_ptr&) = delete;

那么,如果将其作为函数返回值呢?

std::unique_ptr<int> CreateUnique() {
    auto ptr = std::make_unique<int>(0);
    return ptr;
}

int main() {
  CreateUnique






请到「今天看啥」查看全文


推荐文章
贵州省文化和旅游厅  ·  春天去哪儿?DeepSeek力荐贵州!
18 小时前
贵州省文化和旅游厅  ·  春天去哪儿?DeepSeek力荐贵州!
18 小时前
重庆市文化和旅游发展委员会  ·  今年春节泼天的流量,重庆接住了!
昨天
重庆市文化和旅游发展委员会  ·  今年春节泼天的流量,重庆接住了!
昨天
新疆是个好地方  ·  看《封神2》,这个5A级景区免门票!
2 天前
新疆是个好地方  ·  看《封神2》,这个5A级景区免门票!
2 天前
利维坦  ·  关于猫的奇怪研究
8 年前
乐趣微生活  ·  逗比老妈竟玩坏儿子 笑翻天【NO2】
8 年前
百度百家号精选  ·  震惊!“UC震惊部”?
7 年前
美美耶  ·  是我先提出分手的......
7 年前