函数实际上是如何按值返回的?

cir*_*mbo 5 c++ temporary return-by-value

如果我有一个类A(按值返回一个对象),并且两个函数f()和g()只有它们的返回变量不同:

class A
{
    public:
    A () { cout<<"constructor, "; }
    A (const A& ) { cout<<"copy-constructor, "; }
    A& operator = (const A& ) { cout<<"assignment, "; }
    ~A () { cout<<"destructor, "; }
};
    const A f(A x)
    {A y; cout<<"f, "; return y;}

    const A g(A x)
    {A y; cout<<"g, "; return x;}

main()
{
    A a;
    A b = f(a);
    A c = g(a);
}
Run Code Online (Sandbox Code Playgroud)

现在,当我执行该行时A b = f(a);,它输出:

copy-constructor, constructor, f, destructor假设f()中的对象y直接在目的地创建,即在对象b的内存位置创建,并且没有涉及临时值,这很好.

当我执行该行时A c = g(a);,它输出:

copy-constructor, constructor, g, copy-constructor, destructor, destructor,.

所以问题是为什么在g()cant的情况下,对象直接在c的内存位置创建,它在调用f()时发生的方式?为什么在第二种情况下调用另一个复制构造函数(我认为是因为涉及临时)?

Dav*_*e S 7

问题是在第二种情况下,您将返回其中一个参数.鉴于通常参数复制发生在调用者的站点而不是函数内(main在这种情况下),编译器会生成副本,然后在进入后再次强制复制它g().

来自http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/

其次,我还没有找到一个在返回函数参数时会忽略副本的编译器,就像我们的sorted实现一样.当你考虑如何完成这些约定时,它是有道理的:没有某种形式的过程间优化,被排序的调用者不能知道最终将返回参数(而不是其他一些对象),因此编译器必须在堆栈上为参数和返回值分配单独的空间.


Dav*_*eas 3

不同之处在于,在这种g情况下,您返回的是传递给函数的值。该标准明确规定了在 12.8p31 中可以在哪些条件下删除副本,并且不包括从函数参数中删除副本。

基本上问题是参数的位置和返回的对象是由调用约定固定的,并且编译器无法基于实现甚至可能在调用位置不可见)返回的事实来更改调用约定的论点。

我不久前开始了一个短暂的博客(我希望有更多时间......),我写了几篇关于 NRVO 和复制省略的文章,这可能有助于澄清这一点(或者没有,谁知道:)):

值语义:NRVO

值语义:复制省略