tags:

views:

952

answers:

5

E.g.

What's best out of these:

std::string f() {}

or

const std::string& f() {}
A: 

Related to this question, I've just seen this article posted in another question:
http://www.ddj.com/database/184403855

shoosh
That article relates to incoming parameters, not to return values. Return-value optimisation is quite another beast. :-)
Chris Jester-Young
(The answer where I linked to that post _does_ exercise return-value optimisation, so it does work there. There is a variant of RVO called named return-value optimisation, NRVO; that's something quite different to plain-jane RVO. :-P)
Chris Jester-Young
+1  A: 

If you return a reference to a variable that is local to the function then you're going to end up with issues (depending on the compiler and its settings).

If you return a reference to an instance that's still in scope when the function returns then it will be faster, as no copy of the string is being created.

So the latter is more efficient (technically) but may not function as expected depending on what you return a reference to.

OJ
As I mentioned in one of the other comments, be aware of how return-value optimisation and named return-value optimisation work. While sometimes you must return by value for correctness, knowing when RVO and NRVO applies means you can sometimes eliminate the copying overhead.
Chris Jester-Young
RVO and NRVO are just compiler optimizations that take place when the function does return an object...
Nicola Bonelli
+11  A: 

A function should never return a reference to a local object/variable since such objects go out of the scope and get destroyed when the function returns.

Differently the function can return a const or non const reference to an object whose scope is not limited by the function context. Typical example is a custom operator<<:

std::ostream & operator<<(std::ostream &out, const object &obj)
{
   out << obj.data();
   return out;
}

Unfortunately returning-by-value has its performance drawback. As Chris mentioned, returning an object by value involves the copy of a temporary object and its subsequent destruction. The copy takes place my means of either copy constructor or operator=. To avoid these inefficiency smart compilers may apply the RVO or the NRVO optimizations, but there are cases in which they can't -- multiple returns.

The upcoming C++0x standard, partially available in gnu gcc-4.3, introduces the rvalue reference [&&] that can be used to distinguish a lvalue from a rvalue reference. By means of that it's possible to implement the move constructor useful to return an object partially avoiding the cost of copy constructor and the destructor of the temporary.

The move constructor is basically what Andrei envisioned some years ago in the article http://www.ddj.com/database/184403855 suggested by Chris.

A move constructor has the following signature:

// move constructor
object(object && obj)
{}

and it's supposed to take the ownership of the internals of the passed object leaving the latter in a default state. By doing that copies of internals are avoided and the destruction of the temporary made easy. A typical function factory will then have the following form:

object factory()
{
    object obj;
    return std::move(obj);
}

The std::move() returns a rvalue reference from an object. Last but not least, move constructors allow the return-by-rvalue-reference of non-copyable objects.

Nicola Bonelli
I'm not sure at all, but I think your example is wrong. You return a reference to a local object. Change your return type to simply "object". It is an rvalue by itself already, and will move to its destination by prefering the destination objects move constructor.
Johannes Schaub - litb
Ops, right.. thanks for the fix.
Nicola Bonelli
+3  A: 

I would like to add to Nicola's excellent answer. Yes, you must never return a dangling reference (e.g., reference to a local variable), however, there are three useful ways to improve performance in those cases:

  1. Return-value optimisation (RVO): You return by value, but eliminate copying by having only one return statement, which creates the return value on the spot. Here's an example of RVO being used: http://stackoverflow.com/questions/275733/c-string#275747

  2. Named return-value optimisation (NRVO): You return by value, and declare the return value variable first, at the top of the function. All return statements return that variable. With compilers that support NRVO, that variable is allocated in the return-value slot, and does not get copied upon return. e.g.,

    string
    foobar()
    {
        string result;
        // fill in "result"
        return result;
    }
    
  3. Use a shared_ptr or the like as the return type; this necessitates creating your object on the heap, rather than the stack. This prevents dangling-reference problems while still not requiring the whole object to be copied, just the smart pointer.

By the way, I can't take credit for the information about RVO and NRVO; they come straight out of Scott Meyers's More Effective C++. Since I don't have the book with me at the moment, any errors in my description are my doing, not Scott's. :-)

Chris Jester-Young
Right Chris. To complete your answer I'd like you to mention as 4. the move constructor that is already available by means of rvalue reference of the upcoming c++0x :-)
Nicola Bonelli
@Nicola: As I'm not too au fait with C++0x, I think you should write a post mentioning how the move constructor works, and I'll upvote you. :-)
Chris Jester-Young
I'm going to update my answer :)
Nicola Bonelli
Yay! Well, hopefully your updated answer gets accepted as the best answer. :-)
Chris Jester-Young
+1  A: 

I asked an almost identical question a few weeks ago:

http://stackoverflow.com/questions/134731/returning-a-const-reference-to-an-object-instead-of-a-copy

Rob
Not quite; yours involves returning a reference to a member. That is arguably safe in many cases (unless the containing object gets destroyed sooner than the reference is kept around), but this question is more general. :-)
Chris Jester-Young