为什么不执行“部分RVO”?

ead*_*ead 6 c++ optimization rvo

请看一下这个愚蠢的功能,该功能只能说明问题并简化实际代码:

struct A;

A create(bool first){
        A f(21), s(42);
        if(first)
           return f;
        else
           return s;
}
Run Code Online (Sandbox Code Playgroud)

我知道,由于不清楚在编译过程中将返回哪个对象,因此我们不能期望总是执行返回值优化(RVO)。

但是,可能会期望在50%的情况下执行RVO(由于缺少更多信息,假设true/的分布均匀false):只需确定应在哪种情况下执行RVO(first==truefirst==false)并将其应用于此参数值,接受在另一种情况下必须调用复制构造函数。

但是,并非所有的编译器都可以使用这种“部分RVO”功能(请参见gccclangMSVC的实时介绍)-在两种情况下(即first==truefirst==false)都使用了复制构造函数,并且未将其省略。

是否有某种东西会使上述情况下的“部分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);
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 10

让我们考虑一下如果对RVO完成了会发生什么f,这意味着它直接在返回值中构造。如果first==truef被退回,很好,不需要副本。但是如果first==false随后s返回它,那么程序将在析构函数运行之前s在其顶部复制构造。然后,析构函数for 将运行,现在返回值是一个已经被销毁的无效对象!f ff

如果s改为进行RVO操作,则应用相同的参数,但现在问题发生在时first==true

无论您选择哪种方式,都可以在50%的情况下避免复制,而在另50%的情况下获得不确定的行为!这不是理想的优化!

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