14

Yet another "why must std::move prevent the (unnamed) return-value-optimization?" question:

Why does std::move prevent RVO? explains that the standard specifically requires that the function's declared return type must match the type of the expression in the return statement. That explains the behavior of conforming compilers; however, it does not explain the rationale for the restriction.

Why do the rules for RVO not make an exception for the case where the function's return type is T and the type of the return expression is T&&?

I also am aware that implementing such things in compilers doesn't come for free. I am suggesting only that such an exception be allowed but not required.

I also am aware that return std::move(...) is unnecessary since C++11 already requires that move semantics be used when the RVO can't be applied. Nevertheless, why not tolerate an explicit request for optimization instead of turning it into a pessimization?


(Aside: Why are the return-value-optimization and rvo tags not synonyms?)

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
  • "since C++11 already requires that move semantics be used when the RVO can't be applied." – That's a strange way to put it. I'd rather sum it up as "C++11 already requires that move semantics be used when the conditions for RVO apply but the compiler does not implement it."; in particular, if you try to do `return x.y;` where `x` is a local variable, it will be a copy without `std::move` (or a different rvalue cast). – Arne Vogel Mar 20 '18 at 16:16

1 Answers1

19
auto foo() -> T&&;

auto test() -> T
{
    return foo();
}

You say in this case the RVO should be allowed to be applied. However consider this legal implementation of foo:

T val;

auto foo() -> T&&
{
   return static_cast<T&&>(val); // because yes, it's legal
}

The moral: only with prvalues you know for sure you have a temporary and most important you know the exact lifetime of the temporary so you can elide its construction and destruction. But with xvalues (e.g. T&& return) you don't know if that is indeed bound to a temporary, you don't know when that value was created and when it goes out of scope or even if you know you can't change it's construction and destruction point like the above example.

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?

It's not that it is worse. It is just not possible. With your example temp is a local variable in the function where you want to apply NRVO i.e. test. As such it is an object fully "known" in the context of test, its lifetime is known, the normal point of ctor and dtor is known. So instead of creating the temp variable in the stack frame of test it is created in the stack frame of the caller. This means there is no copy of the object from the stack frame of test to the stack frame of the caller. Also please see that in this example foo() is completely irrelevant. It could have been anything in the initialization of temp:

auto test() -> T
{
    T temp = /*whatever*/;

    return temp; // NRVO allowed
}

But with return foo() you can't elide the copy simply because you can't know to what object the return reference binds to. It can be a reference to any object.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 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? – jamesdlin Mar 23 '18 at 00:12
  • Does this mean returning by value can be more efficient than using move semantics to return? My understanding is that RVO might implement the return as an argument reference. Filling this reference object would be faster than moving the object, right? – Zebrafish Mar 23 '18 at 10:27
  • @Zebrafish Directly constructing an object via copy elision avoids doing a copy and avoids doing a move, so yes, it's more efficient than doing a move. – jamesdlin Mar 23 '18 at 16:08
  • @bolov: A request: Could you go to the [`rvo` tag's synonyms page](https://stackoverflow.com/tags/rvo/synonyms) and suggest `return-value-optimization` as a synonym? As jamesdlin pointed out in his question, it's a little silly that they're not already synonyms, but of course, since they're not frequently used, few people have both the rep and the answer score needed to suggest the synonym in the first place. – ShadowRanger Jul 20 '18 at 19:15
  • 1
    @ShadowRanger can't do that as return-value-optimization is suggested as a synonym for copy-elision - which is incorrect in my opinion. Opened meta (sort of, added an answer to a older question): https://meta.stackoverflow.com/a/371346/2805305 – bolov Jul 20 '18 at 20:43
  • @bolov: Good enough. Thanks for looking into it! – ShadowRanger Jul 20 '18 at 21:39