在 C++ 复制省略期间如何管理内存?

How is memory managed during C++ copy elision?

#include <iostream>

using namespace std;

class A
{
public :
    A()
    {
        cout<<"constructor is called"<<endl;
    }
    
    ~A()
    {
        cout<<"destructor is called"<<endl;
    }
    
    A(const A &s)
    {
        cout<<"copy constructor is called"<<endl;
    }
};

A beta()
{
    A a;
    cout<<"mem location a : "<<&a<<endl;
    return a;
}

int main(int argc, char** argv) {
    A b = beta();
    cout<<"mem location b : "<<&b<<endl;
    return 0;
}

以上程序生成以下输出:

constructor is called
mem location a : 0x7ffc12bdaf77
mem location b : 0x7ffc12bdaf77
destructor is called

据我所知,只创建了 A 的一个实例,而不是 A aA b 的 2 个实例,因为复制省略或 return 值优化。 但是从内存的角度来看上面的程序,object a 位于函数 beta 的堆栈激活记录或堆栈 space 中,并且具有内存位置 0x7ffc12bdaf77 。当它 returns 来自 beta 时,复制省略使对象 b 与对象 a 相同而不是复制它。所以,现在 b 也有地址 0x7ffc12bdaf77 。我无法理解 if b 仍然是函数 main 的局部并且存在于它的堆栈中 space,它怎么会占用 main 堆栈之外的内存 space

在 Microsoft x64 调用约定中,一个隐藏指针将作为 beta 的第一个参数添加到前面。该指针包含 b 的地址,它将位于 main 的堆栈帧上。该指针将用于立即在 main 的堆栈帧上同时创建 ab。所以换句话说,a 不存在于 beta 的栈帧上;从内存的角度来看,ab 是等价的。

As far as I understand just a single instance of A was created rather than 2

正确。

how can it occupy a memory outside main's stack space ?

没有。

return 值只是在 main 调用的堆栈帧中创建的。

考虑这一行:A b = beta();。编译器如何实现它?

嗯,beta 是一些其他功能。 beta 的 return 值是 A。所以有两种可能的方法来实现这一点。编译器可以让 beta 为其 A return 值分配堆栈 space,但这可能会有问题,因为调用者需要使用该 return 值。因此,编译器让 caller 为 return 值分配堆栈 space。毕竟,调用者确实知道 return 值的 size/alignment,因此它拥有分配 space.

所需的一切信息

所以让我们选择后者。这意味着当编译器调用 beta 时,它会传入 beta 的 return 值所在的地址。但这也意味着编译器,对于 beta 的这个特定调用,可以只给 beta 的 return 值与它给 b.[=30 的地址相同的地址=]

所以就在那里,我们将函数的 return 值的副本删除到 b

所以当编译器去编译 beta 时,它知道调用者将给它一个指向 return 值应该去哪里的指针。所以 return a; 语句从 a 变量语义上复制到这个 return 值对象中。

但是,编译器可以看到整个 beta。并且可以看出a变量是一个局部变量,在所有的控制路径上都得到了returned。因此,编译器可以将 a 放入调用者为 return 值提供的内存中,而不是给 a 一个单独的堆栈地址。

所以,我们再次将 a 中的一个副本删除到 return 值中。