请看一下这个愚蠢的函数,它只会说明问题和简化真正的代码:
struct A;
A create(bool first){
A f(21), s(42);
if(first)
return f;
else
return s;
}我理解,因为不清楚在编译期间将返回哪个对象,所以不能总是执行返回值优化(RVO)。
但是,在50%的情况下(假设true/false由于缺乏进一步的信息而统一分布)可能会执行RVO :只需决定应该对哪种情况执行RVO (first==true或first==false),并将其应用于此参数-值,并接受在另一种情况下必须调用复制构造函数。
然而,对于我可以使用的所有编译器(参见使用gcc、嘎吱声和MSVC) --在这两种情况下(即first==true或first==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);
}发布于 2019-05-29 10:17:23
让我们考虑一下如果对f执行RVO会发生什么,这意味着它是直接在返回值中构造的。如果first==true和f被返回,那么就不需要拷贝了。但是如果first==false然后返回s,那么程序将在f析构函数运行之前将构造s复制到f的顶部。然后,f的析构函数将运行,现在返回的值是一个已经销毁的无效对象!
如果RVO是为s完成的,则同样的参数也适用,但现在问题发生在first==true时。
无论你选择哪一种,你都会避免在50%的情况下复制,而在其他50%的情况下,你会得到不明确的行为!这不是一个理想的优化!
为了完成这项工作,必须改变局部变量的销毁顺序,以便在将f复制到该内存位置之前销毁s (反之亦然),这是一个非常危险的问题。破坏的顺序是C++的一个基本属性,不应该被篡改,否则你会破坏RAII,谁知道还有多少其他的假设。
发布于 2019-05-29 11:26:28
我对此的看法是,除了饶有兴趣地阅读Jonathan的答案之外,我们始终可以为返回的对象定义一个move构造函数。如果RVO由于任何原因无法应用,并且在我看来是一个很好的解决方案,那么这将比复制构造函数更受青睐。
像std::vector这样的东西定义了这样的构造函数,所以您可以免费获得它。
https://stackoverflow.com/questions/56357604
复制相似问题