There's an application for this pattern that isn't addressed in the answers given, so I thought I'd share.
One common pattern where return-by-reference is useful is when the following are true:
- Your function receives a reference variable
- Your function modifies the reference variable in some way
- You wish to be able to then use the value in further statements as though it were returned
Take this example:
#include <iostream>
int add_two(int operand){
return operand+2;
}
int main(){
int a{3};
std::cout << add_two(a) << std::endl;
return 0;
}
In this example, add_two
receives by copy the value in a. Then it adds two to it, then it returns by copy the result.
For a simple integer, that's fine. No problem. Maybe faster than passing pointers/references, honestly. But if you were sending in a very large structure of some kind, that kind of copying could be onerous or even impossible. Also, it's important to note that we did not change the value of a. So while this will work as expected with respect to outputting a value, a was not changed, and that's one of the desired effects of add_two
.
If we refactor it to this:
#include <iostream>
void add_two(int &operand){
operand += 2;
}
int main(){
int a{3};
add_two(a);
std::cout << a << std::endl;
return 0;
}
Then now we're passing a reference to a into the add_two
, and we're adding two to that. That solves the copying issue: we're no longer copying the hypothetical large data structure, and it adds the missing functionality: a is changed. That's good and all, but we can't use add_two
inline. We have to have two separate lines. So we further refactor:
#include <iostream>
int & add_two(int &operand){
operand += 2;
return operand;
}
int main(){
int a{3};
std::cout << add_two(a) << std::endl;
return 0;
}
Now, we're not copying anything, and since the function doesn't return void, we can still use it elsewhere.
This pattern is very common when creating custom unary operators for data types. The ++()
and --()
(pre-operation) operators both return references to their operands (see here). All of the <<
and >>
operators related to stream functionality receive streams by reference, modify them, then return references to the stream so that they can be strung together.
In general, you must return something that will persist after the function exits. If you simply return a value, that value will persist because it's been copied out. But if you return a reference, that reference has to exist once the function's scope goes away. It's a picky, specific use case, and one that isn't likely to come up often. But this is one example.