If the return type were declared free_throws instead of free_throws &, the same return statement would return a copy of target (and hence a copy of team). But the return type is a reference, so that means the return value is the original team object first passed to accumulate().
What happens next? The accumulate() return value is the first argument to display(), so that means team is the first argument to display(). Because the display() parameter is a reference, that means the ft object in display() really is team. Therefore, the contents of team get displayed. The net effect of
display(accumulate(team, two));
is the same as that of the following:
accumulate(team, two);
display(team);
The same logic applies to this statement:
accumulate(accumulate(team, three), four);
This has the same effect as the following:
accumulate(team, three);
accumulate(team, four);
Next, the program uses an assignment statement:
dup = accumulate(team,five);
As you might expect, this copies the values in team to dup.
Finally, the program uses accumulate() in a manner for which it was not intended:
accumulate(dup,five) = four;
This statement—that is, assigning a value to a function call—works because the return value is a reference. The code won’t compile if accumulate() returns by value. Because the return value is a reference to dup, this code has the same effect as the following:
accumulate(dup,five); // add five's data to dup
dup = four; // overwrite the contents of dup with the contents of four
The second statement wipes out the work accomplished by the first, so the original assignment statement was not a good use of accumulate().
Why Return a Reference?
Let’s look a bit further at how returning a reference is different from the traditional return mechanism. The latter works much like passing by value does with function parameters. The expression following the return is evaluated, and that value is passed back to the calling function. Conceptually, this value is copied to a temporary location and the calling program uses the value. Consider the following:
double m = sqrt(16.0);
cout << sqrt(25.0);
In the first statement, the value 4.0 is copied to a temporary location and then the value in that location is copied to m. In the second statement, the value 5.0 is copied to a temporary location, then the contents of that location are passed on to cout. (This is the conceptual description. In practice, an optimizing compiler might consolidate some of the steps.)
Now consider this statement:
dup = accumulate(team,five);
If accumulate() returned a structure instead of a reference to a structure, this could involve copying the entire structure to a temporary location and then copying that copy to dup. But with a reference return value, team is copied directly to dup, a more efficient approach.
Note
A function that returns a reference is actually an alias for the referred-to variable.
Being Careful About What a Return Reference Refers To
The single most important point to remember when returning a reference is to avoid returning a reference to a memory location that ceases to exist when the function terminates. What you want to avoid is code along these lines:
const free_throws & clone2(free_throws & ft)
{
free_throws newguy; // first step to big error
newguy = ft; // copy info
return newguy; // return reference to copy
}
This has the unfortunate effect of returning a reference to a temporary variable (newguy) that passes from existence as soon as the function terminates. (Chapter 9, “Memory Models and Namespaces,” discusses the persistence of various kinds of variables.) Similarly, you should avoid returning pointers to such temporary variables.
The simplest way to avoid this problem is to return a reference that was passed as an argument to the function. A reference parameter will refer to data used by the calling function; hence, the returned reference will refer to that same data. This, for example, is what accumulate() does in Listing 8.6.
A second method is to use new to create new storage. You’ve already seen examples in which new creates space for a string and the function returns a pointer to that space. Here’s how you could do something similar with a reference:
const free_throws & clone(free_throws & ft)
{
free_throws * pt;
*pt = ft; // copy info
return *pt; // return reference to copy
}
The first statement creates a nameless free_throws structure. The pointer pt points to the structure, so *pt is the structure. The code appears to return the structure, but the function declaration indicates that the function really returns a reference to this structure. You could then use the function this way:
free_throws & jolly = clone(three);