cairol
cairol

Reputation: 8773

Return a const reference or a copy in a getter function?

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?

Upvotes: 43

Views: 30167

Answers (8)

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506905

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.

Upvotes: 8

ergosys
ergosys

Reputation: 49019

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.

Upvotes: 4

ScaryAardvark
ScaryAardvark

Reputation: 2955

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.

Upvotes: 0

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.

Upvotes: 11

ur.
ur.

Reputation: 2947

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?

Upvotes: 2

Will
Will

Reputation: 75625

  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.

Upvotes: 0

Aryabhatta
Aryabhatta

Reputation:

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).

Upvotes: 22

Naveen
Naveen

Reputation: 73443

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.

Upvotes: 19

Related Questions