views:

550

answers:

8

What's better as default, to return a copy (1) or a reference (2) from a getter function?

class foo {
public:
    std::string str () { // (1)
        return str_;
    }

    const std::string& str () { // (2)
        return str_;
    }

private:
    std::string str_;
};

I know 2) could be faster but don't have to due to (N)RVO. 1) is safer concerning dangling references but the object will probably outlife or the reference is never stored.

What's your default when you write a class and don't know (yet) whether performance and lifetime issues matter?

Additional question: Does the game change when the member is not a plain string but rather a vector?

+6  A: 

My rule of thumb is to return a copy for simple basic datatypes such as int, string etc. For a bit more complicated structures where copying may be costlier (like vector you mentioned) I prefer to return a const-reference.

Naveen
I do this too, but I always get nervous that some caller is going to squirrel away a copy of the reference instead of a copy of the string.
Omnifarious
As long as the semantics of the call and object lifespans are appropriate or documented that is not really a problem: if your object has a name and they keep a reference to it knowing that the lifetime of your object is guaranteed for the complete lifetime of the reference, everything is fine. Why require copies?
David Rodríguez - dribeas
+12  A: 

Well it really depends on what you expect the behaviour to be, by default.

Do you expect the caller to see changes made to str_ unbeknownst(what a word!) to them? Then you need to pass back a reference. Might be good if you can have a refcounted data member and return that.

If you expect the caller to get a copy, do 1).

Moron
@Kornel - Moron is correct. The const reference just prevents the caller from modifying the object. If the internal object is changed due to some other reason and the caller initialized a reference from the getter, the caller will see the change.
R Samuel Klatchko
@R S, he may be, but what has "Do you expect the caller to see changes made to str_" to do with the question asked?
Kornel Kisielewicz
@Kornel, asking that behavior question has everything to do with the question asked. A reference, whether const or not will allow the caller to see changes made there. The const is only to prevent the caller from modifying the data, which happens in either case: whether you provide a const reference or a copy. Though, you might have a point about trying to return a refcounted const reference.
Moron
@Kornel - I was primarily replying to your implication that returning a const reference would somehow prevent the caller from seeing changes to the internal object.
R Samuel Klatchko
@Moron - duh, stupid me, yeah I didn't get it :) +1!
Kornel Kisielewicz
@R S, nope, I misunderstood the core concept of observability, not the fact on returning const reference
Kornel Kisielewicz
@Moron - an interesting aside is whether it should be up to the class designer to decide that the caller should have to get a snapshot. If you return a reference, the caller can decide whether to initialize reference or copy the value. I'm not even sure you can describe it as default behavior because the caller has to make the active choice whether to initialize a reference or copy the value to another instance.
R Samuel Klatchko
@Kornel - well, when you emphasized the word const in your original comment (which is now gone), it sure read to me that you thought const would prevent the observability.
R Samuel Klatchko
@R S, no, I mistakenly implied that M. was writing "if you expect the caller to make changes" havn't noticed the word "see", my fault.
Kornel Kisielewicz
Very good point Moron, I never thought about that!
Tom
+1 for keeping the unbeknowst/unbeknown flame war going. Pretty soon now, someone is going to invoke Godwin's Law on http://forum.wordreference.com/showthread.php?t=137197
Mawg
A: 
  1. if its a small basic type - primatives like int and long and their wrappers and other basic things like 'Point' - return a copy

  2. if its a string, or any other complex type - return a reference.

Will
A: 

If there is no special reason to use a value type as return value, I always return a const reference. If I need (or expect to need) a (writable) copy, I add a copy ctor and an assignment operator to the returned class if not already available. For the usage think of:

const MyClass & ref = container.GetAt( 1234 ); // need only reference
MyClass copy = container.GetAt( 1234 ); // get writable copy 

Actually this is quite straight forward, isn't it?

ur
+1  A: 

Returning a reference to an object's internals as part of its public interface can be a code smell if not outright bad design.

Before returning a reference to an internal object in a public interface, the designer should pause. Doing so couples users of your class to part of your design. Often it is outright unnecessary, sometimes it indicates further design work is needed. At times it is necessary, as commenters have noted.

ergosys
Providing access to contained objects is not per se a sign of bad design - e.g. STL containers do it all the time.
Georg Fritzsche
This can be true in other languages, but not necessarily in c++. In c++ you can control what external code does with the objects you return (make it const, and you will guarantee that external code will not break any invariant). Compare that with Java, where the final keyword applies to the reference and not the instance being referred, in Java, unless you are returning some immutable object it is in fact a bad design choice, but C++ is a complete different beast.
David Rodríguez - dribeas
Objects have different expected lifespans as well. Functors for instance, are often (most of the time?) created on the stack as arguments to function calls. for_each(v.begin(), v.end(), mogrify());
swarfrat
As I noted, only if unavoidable. But I've updated my answer to make it clearer what I meant.
ergosys
+4  A: 

The compiler will not be able to perform (N)RVO in this case. The (named) return value optimization is an optimization where the compiler creates the function auto variables in the place of the return value to avoid having to copy:

std::string f()
{
   std::string result;
   //...
   return result;
}

When the compiler sees the code above (and assuming that if any other return is present it will also return the result variable) it knows that the variable result has as only possible fate being copied over the returned temporary and then destroyed. The compiler can then remove the result variable altogether and use the return temporary as the only variable. I insist: the compiler does not remove the return temporary, it removes the local function variable. The return temporary is required to fulfill the compilers call convention.

When you are returning a member of your class, the member must exist, and the call convention requires the returned object to be in a particular location (stack address usually). The compiler cannot create the method attribute over the returned object location, nor can it elide making the copy.

David Rodríguez - dribeas
Sounds reasonable. But isn't it true that with RVO we save one copy? IIRC when I do std::string my_str = my_foo.str() without RVO the string is copied twice (temporary and my_str) but with RVO we get rid of the temporary because the string is copied directly into my_str. Or is that due to some other optimization?
Tom
The RVO (or NRVO for what matters) is performed when compiling the function. When generating the function, the compiler does not know whether the value will be used for copy-initialization, assignment, calling a method, binding to a temporary...
David Rodríguez - dribeas
A: 

The only problem I have with returning a const-reference, which is something I would typically do for non basic types, is that there is nothing to stop the caller removing the "const"ness and then modifying the value.

Personally, I'd suggest that such code is a bug. If they know you're returning a reference and continue to cast away the const then it's on their head.

ScaryAardvark
+4  A: 

I'm returning a reference, because a string seems not "cheap to copy" to me. It's a complex data type with dynamic memory management and all that.

The "if you want the caller to get a copy, you should return by value" argument is moot, because it doesn't preclude copies at all. The caller can still do the following and get a copy anyway

string s = obj.str();

You need to explicitly create a reference on the caller side to be able to refer to the data member directly afterwards - but why would you do that? There definitely are enough user defined types that are cheap to copy

  • Smart Pointers
  • Iterators
  • All of the non-class types.
Johannes Schaub - litb