Returning a Const Reference to an Object Instead of a Copy

Returning a const reference to an object instead of a copy

The only way this can cause a problem is if the caller stores the reference, rather than copy the string, and tries to use it after the object is destroyed. Like this:

foo *pFoo = new foo;
const std::string &myName = pFoo->getName();
delete pFoo;
cout << myName; // error! dangling reference

However, since your existing function returns a copy, then you would
not break any of the existing code.

Edit: Modern C++ (i. e. C++11 and up) supports Return Value Optimization, so returning things by value is no longer frowned upon. One should still be mindful of returning extremely large objects by value, but in most cases it should be ok.

return const reference vs temporary object

Even if you assign it to a const reference, the return value is declared as passed by value, that means it'll be copied[1] to outside as a temporary object, and then binded to the const reference. Binding temporary object to a const reference is fine, the object won't be destroyed until getting out of the lifetime of the const reference.

On the other hand, returning reference of a local variable is illegel. The local variable'll be destroyed when the function returned, that means the outside reference will be dangled.

EDIT

My point is that if assigning a const ref to a returned local variable is legal, then shouldn't assigning a const ref to a returned const ref of a local variable be legal as well?

The point is the 1st case is not assigning a const ref to a returned local variable, it's assigning a const ref to a returned temporary variable. (Which might copied from the local variable.)


[1] The copy might be omitted according to RVO technically.

C++ return by reference and return by const-reference value is copied

The variable returned gets a copy, not a reference, why?

When you use a reference to initialize a value whose type is not a reference you get a copy, for example

#include <cassert>

int main() {
int a = 1;
const int& a_ref = a;
int b = a_ref;
assert(&b != &a);
}

Does the const std::set<uint32_t> & GetValues () const function must contain the double const

The second const is applied as a member function qualifier, it means that the method can be called when the calling instance of your class is const qualified. For example

Test test;
test.GetValues(); // 1
const Test& test_ref = test;
test_ref.GetValues(); // 2

Here 1 will call the non-const version and 2 will call the method qualified with const

Further a const qualified method will not let you return non-const references to it's own values, so you must return a const reference to your member variable m_values.

So if you include the second const then you must include the first const, however if you just make the return type be a const reference then you don't need to make the method const. For example

const std::set<uint32_t>& GetValues() // 1
std::set<uint32_t>& GetValues() { return m_values; } // 2

here 1 is allowed but 2 is not allowed.

The reason this happens if you are curious, is because the implicit this pointer is const qualified to be a pointer to const in const qualified methods.


In order to call the size () function (or any other member of the returned object), is a copy created temporarily or all is resolved with the returned reference?

The size() method will be called on the reference! The best way to test things like this is to try it out in a quick test case https://wandbox.org/permlink/KGSOXDkQESc8ENPW (note that I have made the test a little more complicated than needed just for demonstration)

Is it bad to have three functions, return by value, by const-reference and by reference?

If you are exposing the user to a mutable reference, then the best way to let them make a copy (which is why you had the by-value) method is to let them make a copy themselves by initializing a non-ref qualified set with the returned reference (like you did initially)

Also in your code there is am ambiguity between the following two methods

std::set<uint32_t> GetValues() const
const std::set<uint32_t>& GetValues () const

Because all that is different is the return type, and you cannot overload a function off the return type.

When is it a good idea to return a const reference in C++?

The decision of when to return by-reference vs. by-value is not just a matter of performance, it's a matter of code semantics (although performance usually matters a lot when coding in C++).

Some notable examples of returning by-reference are:

  • a getter is often expected to return a const-reference to the actual member, unless that member is cheap to copy
  • to allow for method or operator chaining, returning a reference to the current object (*this)

The question of when to return by-reference actually boils down to a broader question of how to manage an object's lifetime safely. C++ Core Guidelines on object's lifetimes is a good resource to adhere to.

If the object being referred-to outlives the function invocation, then it's generally safe to return it by-reference.

So:

  • this, class members and objects with static storage duration: safe to return it by-reference
  • locals and function's input arguments: not safe to return by-reference

Regarding input arguments - it applies even to const references, since they can refer to temporaries. For example:

std::string const& badFunc(std::string const& arg) {
return arg; // not a good idea
}

std::string const& x = badFunc("abc");
// now x contains a dangling reference

Returning const reference

string("foo") creates an object of type std::string containing the value "foo" locally in the function. This object will be destroyed at the end of the function. So returning the reference to that object will be invalid once the code leaves that function [1]. So in main, you will never have a valid reference to that string. The same would be true if you created a local variable inside foo.

The WHOLE point of returning a reference is that you don't make a copy, and initializing a reference (string &f = foo() is an initialization) will not create a copy of the original object - just another reference to the same object [which is already invalid by the time the code is back in main]. For many things, references can be seen as "a different name for the same thing".

The lifetime of the referred object (in other words, the actual object the "alias name" refers to) should ALWAYS have a lifetime longer than the reference variable (f in this case).

In the case of bar, the code will make a copy as part of the return string("bar");, since you are returning that object without taking its reference - in other words, by copying the object, so that works.

[1] pedantically, while still inside the function, but after the end of the code you have written within the function, in the bit of code the compiler introduced to deal with the destruction of objects created in the function.

Returning const reference parameter without copying

You can't move from a const value, so the function can't take by const &.

You could do this, so the caller has to supply an rvalue, either moving or explicitly copying lvalues they wish to pass.

heavy_obj return_obj_or_default(heavy_obj&& t, bool ret) {
if(ret) {
return std::move(t);
} else {
return heavy_obj();
}
}

Or you could do this, so the caller doesn't have to do anything special, but it will implicitly copy lvalues.

heavy_obj return_obj_or_default(heavy_obj t, bool ret) {
if(ret) {
return std::move(t);
} else {
return heavy_obj();
}
}

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

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

Is there any way can help code return value is const reference?

You could make A non-copyable, and provide a method to do the copy explicitly.

class A {
private:
A(A const &); // expensive copy
public:
A make_copy() { return *this; }
};

The make_copy method will create the expensive copy, but other code will no be able to make copies implicitly, since the copy-constructor is private.



Related Topics



Leave a reply



Submit