简体   繁体   English

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

[英]Why is “partial RVO” not performed?

Please take a look at this silly function, which should only illustrate the problem and a simplification of the real code: 请看一下这个愚蠢的函数,它应该只说明问题并简化实际代码:

struct A;

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

I understand, that because it is not clear which object will be returned during the compilation, we cannot expect return value optimization (RVO) to be always performed. 我理解,因为在编译期间不清楚返回哪个对象,我们不能指望总是执行返回值优化(RVO)。

However, one maybe could expect RVO to be performed in 50% of the cases (assuming uniform distribution for true / false due to lack of further information): just decide for which case RVO ( first==true or first==false ) should be performed and apply it for this parameter-value, accepting that in the other case the copy constructor must be called. 然而,人们可能期望在50%的情况下执行RVO(假设由于缺乏进一步的信息而实现true / false均匀分布):只需决定RVO( first==truefirst==false )应该是哪种情况执行并将其应用于此参数值,接受在另一种情况下必须调用复制构造函数。

Yet this "partial RVO" is not the case for all compilers I can get my hands on (see live with gcc , clang and MSVC ) - in both cases (ie first==true or first==false ) the copy-constructor is used and not omitted. 然而,对于我可以得到的所有编译器来说,这个“部分RVO”并非如此(请参阅gccclangMSVC ) - 在这两种情况下(即first==truefirst==false )复制构造函数是使用而不是省略。

Is there something, that renders the "partial RVO" in the above case invalid or is this an unlikely case of missed optimization by all compilers? 是否存在导致上述情况下“部分RVO”无效的情况,或者这是否是所有编译器错过优化的不太可能的情况?


Complete program: 完整计划:

#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);
}

Let's consider what happens if RVO is done for f , meaning it is constructed directly in the return value. 让我们考虑如果对f RVO会发生什么,这意味着它是直接在返回值中构造的。 If first==true and f gets returned, great, no copy is needed. 如果first==true并返回f ,那么很棒,不需要复制。 But if first==false then s gets returned instead, so the program will copy construct s over the top of f before the destructor for f has run. 但是如果first==false则会返回s ,所以程序会在f的析构f运行之前将构造s复制到f的顶部。 Then after that, the destructor for f will run, and now the return value is an invalid object that has already been destroyed! 然后, f的析构f将运行,现在返回值是一个已被销毁的无效对象!

If RVO is done for s instead the same argument applies, except that now the problem happens when first==true . 如果对s了RVO,则应用相同的参数,但现在问题发生在first==true

Whichever one you choose, you avoid a copy in 50% of cases and get undefined behaviour in the other 50% of cases! 无论您选择哪一个,在50%的情况下都可以避免复制,并在其他50%的情况下获得未定义的行为! That's not a desirable optimization! 这不是一个理想的优化!

In order to make this work the order of destruction of the local variables would have to be altered so that f is destroyed before copying s into that memory location (or vice versa), and that's a very risky thing to mess with. 为了使这项工作,必须改变局部变量的破坏顺序,以便s复制到该存储器位置之前销毁f (反之亦然),这是一个非常危险的事情。 The order of destruction is a fundamental property of C++ that should not be fiddled with, or you'll break RAII and who knows how many other assumptions. 破坏的顺序是C ++的一个基本属性,不应该被摆弄,或者你将破坏RAII,谁知道有多少其他假设。

My take on this, apart from reading Jonathan Wakely's answer with interest, is that one can always define a move constructor for the object being returned. 除了阅读Jonathan Wakely的答案之外,我对此的看法是,总是可以为返回的对象定义移动构造函数。 This will then be favoured over the copy constructor if RVO cannot be applied for whatever reason and seems to me to be a good solution. 如果RVO由于某种原因无法应用,那么这将比复制构造函数更受青睐,并且在我看来这是一个很好的解决方案。

Things like std::vector define such a constructor, so you get that for free. std::vector类的东西定义了这样的构造函数,所以你可以免费获得它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM