10

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?

Kiril
  • 39,672
  • 31
  • 167
  • 226
  • What happens when the return value goes out of scope (i.e. at the end of the function)? My guess is that the reference points to invalid memory. –  Jul 09 '10 at 22:34
  • 1
    It is unlikely that passing `long` by const reference would be beneficial to passing it by value. In general, this is implementation-specific, but on all architectures I can think of, either both `long` and `long&` will be represented as 32-bit quantities, or both will be 64-bit. So you copy the same amount of data, but now the caller has to compute the address (which means that it also has to have a local, and not just keep in in the register), and the callee has to dereference. At best, the function would get inlined, and the optimizer would drop the reference. – Pavel Minaev Jul 09 '10 at 22:41
  • So I'm safe if I just make the return and the input parameters const (without references, just const values). – Kiril Jul 09 '10 at 22:44
  • 2
    You're safe, but it's also pointless (at least to the caller) - as "top-level" `const` is dropped altogether from arguments on function signatures, and will remain but make no difference for `long` in return type (though it might make a difference for class types). – Pavel Minaev Jul 09 '10 at 22:45
  • @robinjam I think that the behaviour is "undefined" (see http://en.wikipedia.org/wiki/Undefined_behavior and http://www.catb.org/jargon/html/N/nasal-demons.html ). Using MSVC it will point to valid (stack) memory that's now being used for something else (or at least, which is no longer resrved for the local variable), i.e. the memory is readable but may contain a random number (or, may *occasionally* contain a random number depending on whether an interrupt happened to occur and overwrite the memory location with something else). – ChrisW Jul 09 '10 at 23:03

5 Answers5

8

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.

M. Williams
  • 4,945
  • 2
  • 26
  • 27
  • 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)? – Kiril Jul 09 '10 at 22:40
  • 3
    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 Jul 09 '10 at 22:48
  • @Lirik Personally I use `const` only when using `const` references and only in classes. Otherwise they simply make no sense. About optimizing - optimize & profile. References here (in assembly code) can sometimes make things better and sometimes worse. You might also be interested in this: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value – M. Williams Jul 09 '10 at 22:50
8

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
  • 54,973
  • 13
  • 116
  • 224
  • 2
    A pointer / reference to an internal variable *is* valid for the life of the expression, this is why std::string 's .c_str() and operator= works. It is also why const char* monkey = myStr.c_str(); then using the pointer 'monkey' elsewhere in the code is a bad idea. – Chris Huang-Leaver Nov 16 '10 at 13:16
5

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
  • 42,585
  • 14
  • 91
  • 146
3

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
  • 93,612
  • 16
  • 138
  • 200
1

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
  • 11,061
  • 25
  • 80
  • 121
  • +1 for 'great way to make code unreadable', and truly broken IMO. I have worked on such projects, believe me it creates misery :-( – Chris Huang-Leaver Nov 16 '10 at 13:12
  • The question clearly states it is not about program code optimization (or readability) but about *understanding* const references and their uses. And without proof, I don’t trust the “the compiler will optimize it anyway”. You are right about readability - but sometimes you gotta do what you gotta do, especially in a lang like C++. – Kissaki May 23 '12 at 19:44