RVO?

Hi, since i started reading up on c++ i was taught to return from functions by reference to save on temps being made. I read that this is a time saver. But now I have just learned about return value optomisation, and now I am confused about return by reference / return by value. I dont understand why I would be reading that return by reference is better if RVO exists?

Are there only certain objects that RVO works with? Does it only work when a variable is set to equal a function that is returning something, and not when the function is just called? - I do think that is the case since I believe you are making the return object in the variable that has been set equal to the function. But I am not 100% sure, so would like to double check.
i was taught to return from functions by reference to save on temps being made
That's premature optimization. Don't do it. Functions should return what makes sense, which might be a value, and reference, a const reference, or something else. RVO is just a handy way of optimizing what you've already decided to return.
i was taught to return from functions by reference
Worse than inefficient, in the majority of cases, returning by reference is wrong.

Consider these two functions, which are wrong:
1
2
int const& f() { return 42; }
int& g(int x) { return x; }
Can you tell why?

This function is also wrong:
 
int const& h(int const& x) { return x; }

Can you find the problem? A similar error almost appeared in the standard, but it was corrected at the last minute.

Return by value instead: it is both efficient and safe.
Last edited on
Consider these two functions, which are wrong:
int const& f() { return 42; }
int& g(int x) { return x; }
Can you tell why?


I am thinking the problem with the first one, is because 42 is an rvalue therefore no memory address, so you cant have a reference to it?
I am not too sure about the other two though. Is it a scope issue?
Returning from a function (whose return type is a reference type) initializes a reference. For example, within f, the statement
return 42;
initializes a int const& with 42 as the initializer. The newly-initialized reference is what is returned from the function.

References are aliases for objects, which have a lifetime. The hidden int const& initialized by return 42 refers to an object whose lifetime ends at the end of the return statement.

As a result, the function always returns a reference to an object that has already ceased to exist -- a dangling reference.

The first function results in some compiler warnings:
GCC wrote:
warning: returning reference to temporary [-Wreturn-local-addr]
Clang wrote:
warning: returning reference to local temporary object [-Wreturn-stack-address]
MSVC wrote:
warning C4172: returning address of local variable or temporary
S.A.:
http://www.cplusplus.com/forum/general/275104/#msg1187301

The issue with the second function is almost the same. Within g, the statement return x; initializes a hidden int& using x as the initializer. Only the lifetime of the referenced object x is different. It lives until the (imminent) end of the function, and not an instant more. Therefore g also returns a dangling reference.

The second function produces compiler warnings similar to f.

Finally the problem with h is very subtle; the compiler is no help. If the expression e is a prvalue expression, using h(e) to initialize a reference, as in
int const& r = h(42);
Initializes r to refer to a materialized temporary object -- the same object bound to h's formal parameter. r becomes a dangling reference at the semicolon.

The point is that returning by reference is really tricky, even for experienced programmers. It can only be done correctly with a guarantee that the object being referenced lives long enough to be useful.
Last edited on
if you do have a performance critical function you can return a reference to a static item and get faster code. This has its own set of problems, and is best avoided outside of very high performance code, by necessity. I now prefer to simply pass in by reference instead. The performance is just as good and the lifetime of the object is now in the hands of the user, rather than inflexibly locked to the routine and at risk of corruption if you need to call the routine again but had a reference to its last result... oops! Yea, been there, messed that up, stopped doing it... :)

so, if you need this optimization, just pass the item into the function as a reference to begin with, rather than try to do it on the function's return or with other tricks. Its just as good for speed, and better in every way for management (well, it costs you a parameter, but its worth it, and you can hide that with OOP if the extra param is too clunky).
Last edited on
Particularly after C++17:
if you do have a performance critical function (or even if performance is not critical), favour returning a value.
https://en.cppreference.com/w/cpp/language/copy_elision

1
2
3
// void name( std::string& str ) { str = "hello" ; }  // *** bad, and also inefficient, avoid

std::string name() { return "hello" ; }  // good, exploits mandatory copy elision 
That too ^^. Its gotten very rare to need to second guess the compiler (this is a good thing).
Its been a very hard habit to break as I had to do so much of it for so long before '11
Registered users can post here. Sign in or register to post.