为什么 RVO 要求如此严格?

Why are the RVO requirements so restrictive?

又一个"why must std::move prevent the (unnamed) return-value-optimization?"问题:

Why does std::move prevent RVO? 解释说,标准明确要求函数声明的 return 类型必须与 return 语句中表达式的类型相匹配。这解释了符合标准的编译器的行为;但是,它没有解释限制的理由。

为什么RVO的规则不对函数的return类型为Treturn表达式的类型为T&&的情况进行例外处理?

我也知道在编译器中实现这些东西不是免费的。我只是建议允许但不需要这样的例外。

我也知道自 C++11 already requires that move semantics be used when the RVO can't be applied 以来 return std::move(...) 是不必要的。尽管如此,为什么不能容忍一个明确的优化要求,而不是把它变成悲观呢?


(旁白:为什么 return-value-optimizationrvo 标签不是同义词?)

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
}

寓意:只有使用纯右值,你才能确定你有一个临时的,最重要的是你知道临时的确切生命周期,这样你就可以省略它的构建和销毁。但是对于 xvalues(例如 T&& return),您不知道它是否确实绑定到临时值,您不知道该值何时创建以及何时超出范围,甚至不知道你知道你不能像上面的例子那样改变它的构造点和破坏点。

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 的上下文中,它是一个 完全 "known" 的对象,它的生命周期是已知的,ctor 和 dtor 的正常点是已知的。因此,不是在 test 的堆栈帧中创建 temp 变量,而是在调用者的堆栈帧中创建它。这意味着没有从 test 的堆栈帧到调用者的堆栈帧的对象副本。另请注意,在此示例中 foo() 是完全不相关的。它可能是 temp:

初始化中的任何内容
auto test() -> T
{
    T temp = /*whatever*/;

    return temp; // NRVO allowed
}

但是对于 return foo(),您不能简单地删除副本,因为您不知道 return 引用绑定到什么对象。它可以是对任何对象的引用。