4

I have two pieces of code:

int f1(int b)
{
   return b;
}

int & f2(int b)
{
   return b;
}

What is the difference between these functions? I know that the second one returns a reference, but since I can use both functions in the same way, what's the difference?

Edit: Is this function better?

int && f2(int b)
{
   return b;
}

And when should I use functions which return references?

Edit2: Then when should I use functions which return Rvalue references?

user2738748
  • 1,106
  • 2
  • 19
  • 36

3 Answers3

6
int f1(int b) {
   return b;
}

returns the integer b.

int & f2(int b) {
   return b;
}

returns a reference to an integer b that was destroyed when the function returned. In other words, you passed b by value to the function, which means b has an address in the stack frame of the function. Once the function returns, anything in that function's stack frame, including the b that you returned a reference to, no longer exists. So, you have no idea what the reference actually refers to, so you can't use it.

Edit: Your edited function is not better. This would be more correct:

int& f2(int& b) {
   return b;
}

Unless you have a situation like the example @user4581301 gave, you should never return a reference to an object created within the function you are returning from, for the reasons described above!

If you want to pass an object to a function, and have the function do something to that object without ever making a copy of the object, do the following:

void f2(int& b) {
    ... do stuff to b
}
  • How about when the function is a member of a class and returns a reference to a filed which is also a member of the class? – user2738748 Oct 20 '15 at 06:42
  • 1
    Your example having a function returning a reference to a modified reference is not entirely pointless. An example of a similar idiom is ostream& operator << (ostream&,...). I can think of other examples too. – Werner Erasmus Oct 20 '15 at 06:57
  • Thanks. I edited it to point to @user4581301's example, which is a good one! – Alexander Bolinsky Oct 20 '15 at 06:58
6

Consider a simple class that wraps an array solely for the purpose of providing an example of what the OP can do with a returned reference.

class example
{
private:
    int array[]= {1,2,3,4,5,6,7,8,9,0};
public:
    int get(int index)
    {
        return array[index];
    }
    int & get2(int index)
    {
        return array[index];
    }
}

Now we have an example that will not go into the badlands of undefined behaviour and can show you the power of this fully armed and operational reference.

Say we have

example x;

We can call either get function to retrieve a value

int val1 = x.get(1);
int val2 = x.get2(2)

but we can also

x.get2(3) = 30;

because get2 returns a reference we can assign to it and make the assignment stick.

This is invaluable should you want to add an index operator to example

int & operator[](int index)
{
    return array[index];
}

because it allows the expected array behaviour

int val = x[5];
x[6] = 10;

EDIT

Tony D brings up another important feature. Returning a reference returns by reference. In addition to allowing modification of the returned object, this does not make a copy and saves whatever effort would have been consumed by making a copy. For the example case of integers this is moot. The cost of passing an integer and a reference to an integer will either be the same or so close that it shouldn't matter. This is not true of a larger, more complex object that could take a significant amount of effort to copy or an object that cannot or should not be copied.

BigFreakingObject & getBigFreakingObject();

will allow the caller to operate on a BigFreakingObject without incurring the costs of duplicating it. This hands over the keys to the kingdom however and allows the caller to do to BigFreakingObject whatever BigFreakingObject's permissions will allow, and this may conflict with the requirements of BigFreakingObject's owner.

Declaring the reference as const with

const BigFreakingObject & getBigFreakingObject();

or

BigFreakingObject const & getBigFreakingObject();

will provide a reference to a BigFreakingObject but not allow the caller to modify its state, protecting the owner of BigFreakingObject from any unpleasant surprises.

For more details on this, read up on Const Correctness.

user4581301
  • 33,082
  • 7
  • 33
  • 54
  • I understand. But could you please tell me what's wrong with this expression: x.get1(3) = 30; ? Why won't it stick? If array weren't private, I could write array[3] = 30 and it would stick. – user2738748 Oct 20 '15 at 07:06
  • 1
    `get1` is returning by value, so it doesn't return array[3]. It returns a copy of array[3]. The compiler will catch this and warn you if you try to modify it, but if the compiler did allow it, all you would accomplish is changing the value of a copy. The original would be untouched. – user4581301 Oct 20 '15 at 07:13
  • The big add, for me at any rate, to setter and getter methods is they allow for validation. The are self defence for objects. For example, I could add a range check to either of the get methods and throw an exception if index was less than 0 or greater than 9. For a setter, an object can prevent outsiders from changing its state or at least get notification when their state is changed. With a public variable the object has no such protection. – user4581301 Oct 20 '15 at 07:17
  • 3
    @user2738748 it's worth noting that functions can also return `const` references, which don't allow the referenced object to be modified, but do let the value be accessed without copying: that can be more efficient when the objects are large, and is needed for objects that are actively non-copyable (e.g. deleted copy constructor). Even for `const` references, they can only be used during the lifetime of the referenced object (i.e. access to your `f2` function argument would still be broken). – Tony Delroy Oct 20 '15 at 07:29
  • @TonyD thanks. Can't believe I left that detail out. – user4581301 Oct 20 '15 at 18:25
3

In...

int & f2(int b)
{
   return b;
}

...the argument b is an automatic (stack-hosted) copy of the caller-provided value: when f2 returns that stack space is reclaimed. When returning an int&, you're passing back a reference to f2's b variable even as it's memory is made available for reuse. If you make any attempt to access the value using the returned reference, you have undefined behaviour.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252