代码之家  ›  专栏  ›  技术社区  ›  Stephen

在这方面,回报值优化的魔力是什么?

  •  1
  • Stephen  · 技术社区  · 6 年前

    基本上,我所做的如下。我的D类有三个构造函数(default、move、copy)和两个重载赋值操作符(move和copy)。我预计任何D类型对象的创建都会调用五个对象中的至少一个。

    但是,按以下方式创建D对象“d4”不会调用其中任何一个对象:

    D d4( foo() );  // foo returns a D
    

    以下代码再现了我想知道的问题:

    #include <iostream>
    #include <vector>
    #include <cassert>
    using std::cout;
    using std::endl;
        class D {
    public:
        D()
        { cout << "D default"<<endl;}
    
        D(const D& d)
        {
            cout << "D copy" << endl;
        }
    
        D(D&& d)
        {
            cout << "D rval" << endl;
            assert(0);
        }
    
        D& operator=(D&& d)
        {
            cout << "D mv assign" << endl;
            return *this;
        }
    
        D& operator=(const D& d)
        {
            cout << "D copy assign" << endl;
            return *this;
        }
    
        volatile int v;
    };
    
    // return 
    D foo()
    {
        D res;
        cout <<"returning a non const D" << endl;
        return res;
    }
    
    int main()
    {
        D d4(foo());
    
        return 0;
    }
    

    基本上,我假设将调用D(D&D)来创建d4,因为foo()返回一个可能不会获取其地址的临时对象。实际上,只有当-fno elide构造函数禁用了返回值优化时,才是这样。

    但是,如果未指定,则默认情况下,RV优化即使在-O0上也会打开。那么,我看到的是:

    D default
    returning a non const D
    

    我从stdout看到的都是foo()。d4本身的创建并没有给我任何帮助。这与我的预期不同。

    我预期会出现以下情况。返回值的内存空间是在调用者的堆栈中分配的,而不是在被调用者的堆栈中分配的。调用默认构造函数来接触内存空间。不会从被调用方堆栈复制到调用方堆栈。然后,在调用方堆栈中分配另一个内存空间。由于返回的值是一个右值,因此调用move构造函数在“调用者堆栈中的另一个内存空间”上写入内容

    我知道这可能需要冗余的内存空间。然而,特别是在我的示例中,move构造函数将与assert(0)一起消亡。但它会让程序继续。结果,返回值优化在程序输出方面产生了差异。

    这是预期的吗?如果是,背后的原因是什么?我已经测试了g++-7.3.0和clang++-5.0.1。他们是一样的。

    2 回复  |  直到 6 年前
        1
  •  2
  •   con ko    4 年前

    因为您使用的是承诺RVO的C++17,所以即使您添加了-O0。 this maybe help

        2
  •  2
  •   eerorika    6 年前

    我预期会出现以下情况。返回值的内存空间是在调用者的堆栈中分配的,而不是在被调用者的堆栈中分配的。调用默认构造函数来接触内存空间。不会从被调用方堆栈复制到调用方堆栈。

    好的,到目前为止还不错。

    然后,在调用方堆栈中分配另一个内存空间。由于返回的值是一个右值,因此调用move构造函数在“调用者堆栈中的另一个内存空间”上写入内容

    啊,但这里你想错了。你看,RVO并不是唯一一种拷贝省略。也可以省略从临时返回值复制局部变量的初始化,它是。因此,没有“调用者堆栈中的另一个内存空间”,因为对象直接构造到变量的内存位置。

    这是预期的吗?

    应该预料到会发生拷贝省略。在您不能依赖复制/移动构造函数来 有副作用。

    推荐文章