另一个“为什么必须 std::move
阻止(未命名的)返回值优化?”问题:
Why does std::move prevent RVO?说明标准特别要求函数声明的返回类型必须与 return
语句中的表达式类型匹配。这解释了符合标准的编译器的行为;但是,它没有解释限制的理由。
为什么RVO的规则对于函数返回类型为T
且return
表达式类型为T&&<的情况不做异常(exception)处理
?
我也知道在编译器中实现这些东西并不是免费的。我只是建议允许但不是必需的这种异常(exception)。
我也知道 return std::move(...)
是不必要的,因为 C++11 already requires that move semantics be used when the RVO can't be applied .然而,为什么不容忍明确的优化请求,而不是将其变成悲观化呢?
(旁白:为什么 return-value-optimization
和 rvo
标签不是同义词?)
最佳答案
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&&
return),您不知道它是否确实绑定(bind)到临时值,您不知道该值何时创建以及何时超出范围,或者即使您知道你不能像上面的例子那样改变它的构造和销毁点。
I'm not sure that I fully understand. If RVO were allowed to be applied to
test()
, why would that be worse than if test did:T temp = foo(); return temp;
which would allow NRVO?
并不是说它更糟。这是不可能的。在您的示例中,temp
是要应用 NRVO 的函数中的局部变量,即 test
。因此,它是 test
上下文中完全“已知” 的对象,它的生命周期是已知的,ctor 和 dtor 的法线点是已知的。因此,它不是在 test
的堆栈帧中创建 temp
变量,而是在调用者的堆栈帧中创建。这意味着从 test
的堆栈帧到调用者的堆栈帧没有对象的拷贝。另请注意,在此示例中 foo()
是完全不相关的。它可能是 temp
的初始化中的任何内容:
auto test() -> T
{
T temp = /*whatever*/;
return temp; // NRVO allowed
}
但是使用 return foo()
你不能仅仅因为你不知道返回引用绑定(bind)到哪个对象而忽略了拷贝。它可以是对任何对象的引用。
关于c++ - 为什么 RVO 要求如此严格?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49380956/