CS 70

Functions Can Return References

We've now seen that whenever a function returns, it's either providing initialization for a named variable, or initializing a secret temporary variable.

So perhaps it isn't a huge surprise to know that we can also have a function return a reference to set up a reference variable (either an actual variable, or a secret temporary one).

  • Goat speaking

    Uh, I don't think it's a huge surprise because we've already seen it.

  • Cat speaking

    Yes—assignment operators have to return a reference.

  • Hedgehog speaking

    I remember, too! We had to say return *this; to return the current object. It all seemed to be a bit odd, but I go along to get along.

  • Horse speaking

    Hay, yeah—I saw an example in the current homework, too: operator[] returns a reference to an element of an array.

  • LHS Cow speaking

    Indeed. But now we're going to think about that code more deeply and think about what's actually going on.

Let's consider these two pieces of code and what they do.

int maxVal(int lhs, int rhs) {
    if (lhs > rhs) {
        return lhs;
    } else {
        return rhs;
    }
}

int main() {
    int x = 41;
    int y = 7;
    int z = maxVal(x,y);
    ++z;

    cout << "main's x is at address " << &x
         << ", and holds " << x << endl;
    cout << "main's y is at address " << &y
         << ", and holds " << y << endl;
    cout << "main's z is at address " << &z
         << ", and holds " << z << endl;

    return 0;
}

This code prints

main's x is at address s1, and holds 41
main's y is at address s2, and holds 7
main's z is at address s3, and holds 42

Notice that x and z refer to different locations in memory and have different values.

Experiment!

int& maxRef(int& lhs, int& rhs) {
    if (lhs > rhs) {
        return lhs;
    } else {
        return rhs;
    }
}

int main() {
    int x = 41;
    int y = 7;
    int& z = maxRef(x,y);
    ++z;

    cout << "main's x is at address " << &x
         << ", and holds " << x << endl;
    cout << "main's y is at address " << &y
         << ", and holds " << y << endl;
    cout << "main's z is at address " << &z
         << ", and holds " << z << endl;

    return 0;
}

This code prints

main's x is at address s1, and holds 42
main's y is at address s2, and holds 7
main's z is at address s1, and holds 42

Notice that here x and z refer to the same location in memory. When we ran ++z it had the same effect as saying ++x (but if y had been larger, we would have modified y).

Experiment!

For our first example, can we say ++maxVal(x,y); ?

It would cause a compiler error. clang++ would say

maxval.cpp:18:5: error: expression is not assignable
    ++maxVal(x,y);
    ^ ~~~~~~~~~~~

and g++ would say

maxval.cpp: In function 'int main()':
maxval.cpp:18:13: error: lvalue required as increment operand
   18 |     ++maxVal(x,y);
      |       ~~~~~~^~~~~

The compilers complain because—usually—trying to modify a temporary value in this way is a mistake, so it's prohibited.

  • Bjarne speaking

    But you can call a non-const member function on a temporary object.

  • LHS Cow speaking

    Yes, that's true. So we could say Cow{}.feed(); to create a temporary (default) Cow, feed it, and throw it away.

  • Cat speaking

    This seems a bit inconsistent. We can't modify temporary objects, except for the times when we can??

  • Bjarne speaking

    Err… It's part of our heritage…?

In the second code sample, can we say ++maxRef(x,y); ?

Writing

++maxRef(x,y);

is basically the same as writing

{
    int& _temp1 = maxRef(x,y);
    ++_temp1;
}

This behavior is, essentially, what we already saw with z. _temp1 is an alias for whichever of x or y holds the largest value, so in this case, we increment x.

New Memory Diagram Rule

When you want to be clear about which thing a function is returning a reference to, then create a new reference, _retval, just as the function returns. You'll immediately cross _retval out once you get back to the calling function, but you might also choose to give it a new name, as we did with z in the code above.

(When logged in, completion status appears here.)