When to Return References (and When Not To)
The advantages of using references for returning results are similar to those for passing arguments as references:
- Returning a (non-
const
) reference lets you modify a pre-existing thing. - Returning a reference can save time by not needing to copy data around.
Pitfalls
What's wrong with the following code?
double& pi() {
double val = 3.1415926535897931;
return val;
}
Besides the fact that maybe we shouldn't return a value that can be modified for this particular function, what else is an issue?
Don't Return a Reference to a Local Variable!
We can see this issue in this slightly larger program:
#include <iostream>
// This function is fine. It returns a reference to something that already
// existed outside the function.
double& changeToPi(double& changeMe) {
changeMe = 3.1415926535897931;
return changeMe;
}
// This function has a problem. It returns a reference to a local variable.
// By the time the function returns, this value will have been destroyed and
// deallocated!
double& pi() {
double val = 3.1415926535897931;
return val;
}
int main() {
double myDouble = 123.456;
std::cout << "This is fine: " << changeToPi(myDouble) << std::endl;
std::cout << "Uh oh, problem: " << pi() << std::endl;
return 0;
}
If we compile this code in our Docker image, we get a compiler warning:
cs70 DOCKER > clang++ -O -o example example.cpp
example.cpp:14:12: warning: reference to stack memory associated with local variable 'val' returned [-Wreturn-stack-address]
return val;
^~~
1 warning generated.
Hay! Wait a minute—you got a warning even though you didn't say all that
-Wall -Wextra -pedantic
stuff?That's right. Those options turn on a variety of helpful warnings, but this issue is so severe and so obviously a problem that the compiler issues a warning anyway.
Why isn't it an error, then?
Uh….
Yeah, I know, something-something-C++'s heritage. Meh.
The main thing is, the compiler's warnings matter. Don't ignore them.
If we try running the program, it doesn't work properly:
cs70 DOCKER > ./passThruPi
This is fine: 3.14159
Uh oh, problem: 1.25548e+232
If you want to see why this problem happens, you could draw a memory diagram, or just watch the video below. (Or both!)
(When logged in, completion status appears here.)