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

C++ 在这方面,回报值优化的魔力是什么?,c++,constructor,return-value-optimization,C++,Constructor,Return Value Optimization,基本上,我所做的如下。我的类D有三个构造函数(default、move、copy)和两个重载赋值运算符(move和copy)。我期望任何D类型对象的创建都会调用五个对象中的至少一个 但是,按如下方式创建D对象“d4”不会调用其中任何一个: D d4( foo() ); // foo returns a D 以下代码再现了我想知道的问题: #include <iostream> #include <vector> #include <cassert> usi

基本上,我所做的如下。我的类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;
}
我从stdout看到的都是foo()。d4本身的创造并没有给我任何东西。这与我预期的不同

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


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


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

因为您使用C++17,它承诺了RVO,即使您添加了-O0。
因为您使用C++17,它承诺了RVO,即使您添加了-O0。

我预料到了以下几点。返回值的内存空间是在调用者的堆栈而不是被调用者的堆栈中分配的。调用默认构造函数来触摸内存空间。不会从被调用方堆栈复制到调用方堆栈

好的,到目前为止还不错

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

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

这是预期的吗

应该预料会发生拷贝省略。在不能依赖复制/移动构造函数不产生副作用的情况下,不应该期望复制省略会发生

我预料到了以下几点。返回值的内存空间是在调用者的堆栈而不是被调用者的堆栈中分配的。调用默认构造函数来触摸内存空间。不会从被调用方堆栈复制到调用方堆栈

好的,到目前为止还不错

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

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

这是预期的吗


应该预料会发生拷贝省略。不应该期望复制省略会发生,因为您不能依赖复制/移动构造函数来避免副作用。

但是,我认为这个()是重复的。假设我正确理解了这个问题。基本上是这样的:“因此,返回值优化在程序输出方面产生了差异。”事实上。您将在前面评论中链接的文档中发现,复制省略“可以改变可观察到的副作用”。在C++ 14之前,它是唯一允许进行这种事情的优化(因为只有分配Eclipse/扩展也是),所以不必害怕C++优化,从而打破你在这个特定的外部的副作用。假设我正确理解了这个问题。基本上是这样的:“因此,返回值优化在程序输出方面产生了差异。”事实上。您将在前面评论中链接的文档中发现,复制省略“可以改变可观察到的副作用”。在C++ 14之前,它是唯一允许进行这种事情的优化(因为只有分配/扩展),所以不必害怕C++优化,从而打破你在这个特定的外部的副作用。你没有冒犯我,只是对一些想象的微小的反应过度。你没有冒犯我,只是对一些想象中的轻微反应过度。
D default
returning a non const D