views:

190

answers:

5

I'm trying to understand if there is any benefit to returning a const reference. I have a factorial function that normally looks like this:

unsigned long factorial(unsigned long n)
{
    return (n == 0) ? 1 : n * factorial(n - 1);
}

I'm assuming that there will be a performance increase when we pass by const reference and we return a const reference... but const-correctness always confuses me.

const unsigned long & factorial(const unsigned long& n)
{
    return (n == 0) ? 1 : n * factorial(n - 1);
}

Is it valid to return a const reference? Furthermore, could somebody please tell me: is it beneficial?

+6  A: 

This is invalid. You can't return reference to a local variable.

MSVS C++ compiler even gives the following warning:

main.cc : warning C4172: returning address of local variable or temporary

Not quite sure about GCC, but probably the result would be the same.

Kotti
OK, but it's safe to assume that a valid optimization would be to make the input parameter const reference, because I've heard that there is not much benefit to passing const by reference for built-in types (such as long). And I should at least make the return type const (without the reference)?
Lirik
The rule of thumb is: when you know the type - pass primitives by value, pass classes encapsulating a single primitive by value, pass everything else by const reference. For opaque third-party classes, tell whatever their documentation tells you to do (e.g. iterators - pass by value). When you don't know the type in advance (e.g. it's a template type parameter), pass by const reference and hope that optimizer will strip it where it's not needed :)
Pavel Minaev
Kotti
+5  A: 

The const reference is incorrect here - you're returning a reference to a local variable - an un-named temporary here, either 1 or the result of n * factorial(n - 1). Because the reference is to a local variable in the function, by the time the reference gets to the caller, that local variable has already gone out of scope, and is invalid.

Return a const reference to large structured types that you want to avoid a copy to when the reference will survive the exit of the function. Usually, this means returning a reference to an argument or to a member variable (in the case of classes).

Thanatos
+5  A: 

A const reference isn't faster than a value, if the size of the value is small. In this case the type of the value is long, which is IMO small (e.g. 4 to 8 bytes): so a const reference will be no faster. In fact it may be slower, because to get the value of a reference the compiler may need to emit the code that will dereference the reference (like dereferencing a pointer).

Given that a reference is implemented (internally) like a pointer, I'd expect to get better performance from passing references than from passing values when the size of the value is bigger than the size of a pointer (assuming that it's even legal to pass a reference: a reference to a local variable that's gone out of scope isn't legal).

ChrisW
A: 

Possible yet I'd never do that. Why?

Because this is a good way to make your code non-readable.

Additionally, the compiler will optimize it without this hack.

And additionally, don't you have other "bottlenecks" to optimize at your program?
I mean, if you take this portion of your code and look at it in assembly you'll see that passing the function the value and getting the result is merely a few opcodes. How?
Well, a 32bit integer will fit in a register. Quick as "mov eax, ...".
On the other hand, you program probably has other design/algorithm issues which might be optimized... Unless it's as simple as an "hello world" program.

So, getting down to this things isn't something I'd do, and everyone are welcome to challenge me.

Poni
A: 

The only case where it is valid to return by const reference is if the object you are returning will outlive the function call* (such as returning a member of the class on which the function has been invoked), and even in that context, whether one should do that is dubious, since that will allow two different callers to access the same memory location, which makes things a nightmare to fix if you use multiple threads.

*NOTE: In your case, the item you are returning is a local variable and therefore will not outlive the function call. Hence the code you have provided is invoking the nefarious undefined behavior.

Michael Aaron Safyan