【问题标题】:When the Return Value Optimization Required/Apply?何时需要/应用返回值优化?
【发布时间】:2015-08-27 15:44:51
【问题描述】:

我对返回值优化有点困惑,这里是例子。

#include <iostream>
using namespace std;
class A{
    int x;
    public:
        A(int val=0):x(val){
            cout << "A" << x << endl;
        }
        A(const A& a){
            x=a.x;
            cout << "B " << x << endl;
        }
        void SetX(int x){
            this -> x=x;
        }
        ~A(){
            cout << "D " << x << endl;
        }
};
A f(A a){
    cout << "C " << endl;
    a.SetX(100);
    return a;
}
int main(){
    A a(1);
    A b=f(a); // Why Copy constructor instead of RVO?
    b.SetX(-100);
    return 0;
}

输出

A 1 // ok
B 1 // ok
C // ok
B 100 // why it's here? Why copy constructor instead of RVO?
D 100 // why after the above line? it should be before the above line.
D -100 // ok 
D 1 // ok

我对 B 100 和 D 100 输出有点困惑。

1) 为什么编译器给出B 100 的输出应该是RVO(不应该调用复制构造函数)。

2) 第二个是如果调用复制构造函数,那么D 100 应该在B 100 之前,因为在fun() 中,函数对象超出了范围。在A b=f(a); 声明之前。

【问题讨论】:

    标签: c++


    【解决方案1】:

    首先,从不需要 RVO。它是允许的,这使得它有很大的不同。

    编辑。

    关于构造函数的顺序,我最初的回答是不正确的。我错过了输出中的那一行,实际上,它只是意味着复制省略没有完全发生。在 3 个语义副本中(通过返回 a 从 f() 到临时位置;从临时位置到 b=f(a) 的右侧;从右侧到 b)一个没有被省略并且完整复制。我不知道是哪个。

    【讨论】:

    • 由于按值传递到f 的副本不是OP所询问的。 OP 正在询问第二个副本。
    • 我已经编辑了我的问题以尝试回答这个问题。现在有意义吗?
    • @SergeyA 是的,您的意思是调用语句结束时fun()'s 对象销毁?当A b=f(a);结束时意味着对象销毁?
    • 其实在那之前就被销毁了。这样想 - 编译器在 f() 中的 } 之前插入对析构函数的调用。
    • @SergeyA 再次感到困惑,那么为什么要在析构函数之前复制构造函数B 100D 100 :( :(
    【解决方案2】:

    RVO 或更具体地说,命名为返回值优化 (NRVO),是一种复制省略,根据标准,在以下情况下是允许的 [12.8/31]:

    在具有类返回类型的函数的 return 语句中,当表达式是 具有与函数返回类型相同的 cv 非限定类型的非易失性自动对象(函数以外或catch子句参数),复制/移动操作可以是通过构造省略 自动对象直接转化为函数的返回值[强调我的]

    因此,在这种情况下不允许使用 RVO,因为 a 是函数参数。

    在函数结束之前但在获取a 的副本作为返回值之后,它必须销毁临时变量a

    【讨论】:

    • 请注意,它在 C++03 中是允许的,这个限制是在 C++11 中引入的,除了难以实现之外,没有任何理由。
    • @Chris 你的意思是编译器会将a 对象复制到b 然后它会销毁到a
    • @LetDoIt 有效,是的。
    • @ChrisDrew 为什么 A b=fun(); 的复制构造函数和析构函数没有在 DEV C++ 中的代码 codepaste.net/rb98wj 中调用?即使上面的代码是相同的(除了值)。并为b object 调用析构函数和复制构造函数。
    • @LetDoIt 在这种情况下,obj 不是函数参数,因此根据标准允许使用 NRVO。
    【解决方案3】:

    1) 为什么编译器给 B 100 输出它应该是 RVO(不应该调用复制构造函数)。

    是的,应该调用它,因为您将获得 a 的副本作为参数。

    100 天

    发生是因为在 return 语句之后,a 超出范围。

    【讨论】:

    • 您的意思是 Object 在语句之后超出范围?在这个声明之后是什么意思? A b=f(a); 因此在析构函数之前调用了复制构造函数?
    • 不,我的意思是就像我写的那样:在f 中,在return a 之后,a 超出范围。
    • 是的.. 那么为什么 D 100 在 B 100 之后?如果return a 超出范围。然后在应该调用 Copy 构造函数之后调用 D 100。你明白我的问题了吗?
    • 嘿,在我评论它之前你已经修复了你的答案:)
    • @SergeyA:是的,这是一个错字。我正在用我的一半大脑煮咖啡。啊啊咖啡……
    猜你喜欢
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多