又一个"为什么std::move
必须阻止(未命名)返回值优化?"问题:
Why does std::move prevent RVO?解释说,该标准特别要求函数的声明返回类型必须与return
语句中表达式的类型匹配。这解释了符合编译器的行为;但是,它没有解释限制的理由。
为什么RVO的规则在函数的返回类型为T
且return
表达式的类型为T&&
的情况下不会例外?< / p>
我也知道在编译器中实现这些东西并不是免费的。我建议只允许这样的例外但不是必需的。
我也知道自C ++ 11 already requires that move semantics be used when the RVO can't be applied以来return std::move(...)
是不必要的。然而,为什么不容忍明确的优化请求而不是将其变成悲观化?
(旁白:为什么return-value-optimization
和rvo
标签不是同义词?)
答案 0 :(得分:19)
auto foo() -> T&&;
auto test() -> T
{
return foo();
}
你说在这种情况下应该允许应用RVO。但请考虑foo
的合法实施:
T val;
auto foo() -> T&&
{
return static_cast<T&&>(val); // because yes, it's legal
}
道德:只有prvalues你肯定知道你有一个临时的,最重要的,你知道临时的确切寿命,所以你可以忽视它的建设和破坏。但是使用xvalues(例如T&&
返回),您不知道这是否确实与临时绑定,您不知道该值何时创建以及何时超出范围,或者即使你知道你不能像上面的例子那样改变它的构造和破坏点。
我不确定我完全理解。如果允许RVO 应用于
test()
,为什么会比测试更糟糕:T temp = foo(); return temp;
哪个会允许NRVO?
并不是说情况更糟。这是不可能的。您的示例temp
是您要应用NRVO的函数中的局部变量,即test
。因此,在test
的上下文中,它是完全&#34;已知&#34; 的对象,其生命周期已知,ctor和dtor的正常点已知。因此,不是在temp
的堆栈帧中创建test
变量,而是在调用者的堆栈帧中创建它。这意味着从test
的堆栈帧到调用者的堆栈帧没有对象的副本。另请注意,在此示例中foo()
完全无关紧要。它可能是temp
的初始化中的任何内容:
auto test() -> T
{
T temp = /*whatever*/;
return temp; // NRVO allowed
}
但是使用return foo()
,您不能仅仅因为您无法知道返回引用所绑定的对象而忽略该副本。它可以是任何对象的引用。