I would not consider performance as the highest priority when making this decision. Anyhow there is RVO and in general one should not optimize before profiling.
That said, imho the biggest difference is how the calling code looks:
int f = foo();
vs
int x;
bar(x);
When calling a function that returns a value, the intention is perfectly clear at the calling code. Even if you dont know what foo
is doing, you dont have to look it up to know it returns an int
and takes zero parameters. On the other hand, look at the call to bar
. What does it do? Does it change the value of a
? The call to bar
might be inside a larger block of code and to understand that block of code you have to look up what bar
is actually doing. It can be even worse if you mix both:
int foobar(int& x);
I would not suggest you to ever write a function like that, because in the calling code
int x;
int y = foobar(x);
one would expect that x
is just a parameter that does not change. On top of that, often the caller is forced to write more code for a simple function call, because he has to declare the return value before calling the function.
Conclusion: Passing a refernce to return values does obfuscate the calling code.
On the other hand, there can be cases where passing a reference is the way to go. A pattern I use from time to time is the following:
struct MooInput {};
struct MooResult{};
void moo(const MooInput& a, MooResult& b);
With the proper names now the caller at least has a chance to write code such that it is clear what is input and what is returned:
MooInput a;
MooOutput b;
void moo(a,b);
PS: The considerations are a bit different if the function takes a parameters, performs some operations on it and returns the result, e.g.
int Boo ( int x) { return x++;}
in that case I often tend to provide also a:
void Coo(int& x) { x++; }
Because the caller might need one of the two versions depending on whether he needs the old value after calling the function or not. If there is only Boo
and the caller does not need the old value, he is forced to make a copy (actually an assignment):
int x;
x = Boo(x);
while if there is only Coo
and he needs the old value he also needs to make a copy:
int x;
int y = x;
Coo(x);