【问题标题】:Why is "partial RVO" not performed?为什么不执行“部分 RVO”?
【发布时间】:2019-05-29 09:44:24
【问题描述】:

请看一下这个傻函数,它应该只是说明问题和对真实代码的简化:

struct A;

A create(bool first){
        A f(21), s(42);
        if(first)
           return f;
        else
           return s;
}

我了解,由于不清楚编译期间将返回哪个对象,我们不能期望总是执行返回值优化 (RVO)。

但是,人们可能期望在 50% 的情况下执行 RVO(假设 true/false 由于缺乏进一步信息而均匀分布):只需决定哪种情况 RVO(first==true 或first==false) 应该被执行并将其应用于此参数值,接受在其他情况下必须调用复制构造函数。

然而,这种“部分 RVO”并非适用于我可以使用的所有编译器(参见 gcc、clang 和 MSVC 的现场直播)——在这两种情况下(即 first==true 或 @987654330 @) 使用了复制构造函数,没有省略。

有什么东西导致上述情况下的“部分 RVO”无效,或者这是所有编译器都错过优化的不太可能的情况?


完整的程序:

#include <iostream>

struct A{
    int val;
    A(int val_):val(val_){}
    A(const A&o):val(o.val){
        std::cout<<"copying: "<<val<<"\n";
    }
};

A create(bool first){
        A f(21), s(42);
        if(first)
           return f;
        else
           return s;
}

int main(){
    std::cout<<"With true: ";
    create(true);
    std::cout<<"With false: ";
    create(false);
}

【问题讨论】:

  • 在这种(不可否认的)简单情况下,一个简单的解决方法是使用 return 语句构建对象“内联”。如if (first) return A(21); else return A(42);
  • 更好的是return A(first ? 21 : 42); ;-)

标签: c++ optimization rvo


【解决方案1】:

让我们考虑一下如果为f 执行 RVO 会发生什么,这意味着它是直接在返回值中构造的。如果 first==true 和 f 被返回,很好,不需要复制。但是如果first==false 则返回s,因此程序将在f 的顶部复制构造s 之前 f 的析构函数已经运行。之后f的析构函数就会运行,现在返回值是一个已经被销毁的无效对象!

如果为 s 完成 RVO,则适用相同的参数,但现在问题发生在 first==true 时。

无论您选择哪一种,在 50% 的情况下会避免复制,而在其他 50% 的情况下会出现未定义的行为!这不是一个理想的优化!

为了完成这项工作,必须更改局部变量的销毁顺序,以便在将s 复制到该内存位置之前销毁f(反之亦然) ,这是一个非常冒险的事情。破坏顺序是 C++ 的一个基本属性,不应该被摆弄,否则你会破坏 RAII,谁知道还有多少其他假设。

【讨论】:

    【解决方案2】:

    除了感兴趣地阅读 Jonathan Wakely 的回答之外,我对此的看法是,人们总是可以为返回的对象定义一个移动构造函数。如果由于任何原因无法应用 RVO,这将比复制构造函数更受青睐,并且在我看来这是一个很好的解决方案。

    像std::vector 这样的东西定义了这样一个构造函数,所以你可以免费得到它。

    【讨论】:

      猜你喜欢
      • 2014-11-15
      • 2013-03-01
      • 1970-01-01
      • 2012-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-09
      • 2017-03-26
      相关资源
      最近更新 更多