Why would one use func( const Class &value )
rather than just func( Class value )
? Surely modern compilers will do the most efficient thing using either syntax. Is this still necessary or just a hold over from the days of non-optimizing compilers?
Just to add, gcc will produce similar assembler code output for either syntax. Perhaps other compilers do not?
Apparently, this is just not the case. I had the impression from some code long ago that gcc did this, but experimentation proves this wrong. Credit is due to to Michael Burr, whose answer to a similar question would be nominated if given here.
There are 2 large semantic differences between the 2 signatures.
The first is the use of
&
in the type name. This signals the value is passed by reference. Removing this causes the object to be passed by value which will essentially pass a copy of the object into the function (via the copy constructor). For operations which simply need to read data (typical for aconst &
) doing a full copy of the object creates unnecssary overhead. For classes which are not small or are collections, this overhead is not trivial.The second is the use of
const
. This prevents the function from accidentally modifying the contents of value via thevalue
reference. It allows the caller some measure of assurance the value will not be mutated by the function. Yes passing a copy gives the caller a much deeper assurance of this in many cases.The first form doesn't create a copy of the object, it just passes a reference (pointer) to the existing copy. The second form creates a copy, which can be expensive. This isn't something that is optimized away: there are semantic differences between having a copy of an object vs. having the original, and copying requires a call to the class's copy constructor.
For very small classes (like <16 bytes) with no copy constructor it is probably more efficient to use the value syntax rather than pass references. This is why you see
void foo(double bar)
and notvoid foo(const double &var)
. But in the interests of not micro-optimizing code that doesn't matter, as a general rule you should pass all real-deal objects by reference and only pass built-in types likeint
andvoid *
by value.The reason that an optimizing compiler can't handle this for you is the issue of separate compilation. In C++, when the compiler is generating code for a caller, it may not have access to the code of the function itself. The most common calling convention that I know of usually has the caller invoke the copy-constructor which means it's not possible for the compilation of the function itself to prevent the copy constructor if it's not necessary.
There is a huge difference which nobody has mentioned yet: object slicing. In some cases, you may need
const&
(or&
) to get correct behavior.Consider another class
Derived
which inherits fromClass
. In client code, you create an instance ofDerived
which you pass tofunc()
. If you havefunc(const Class&)
, that same instance will get passed. As others have said,func(Class)
will make a copy, you will have a new (temporary) instance ofClass
(notDerived
) infunc
.This difference in behavior (not performance) can be important if
func
in turn does a downcast. Compare the results of running the following code:The only time that passing a parameter by value is preferable is when you are going to copy the parameter anyway.
Or
In this case the second example is the same speed as the first if the value parameter is a regular object, but faster if the value parameter is a R-Value.
Although most compilers will do this optimisation already I don't expect to rely on this feature till C++0X, esp since I expect it could confuse most programmers who would probably change it back.
See Want Speed? Pass by Value. for a better explaination than I could give.
The compiler doesn't compile what you "mean", it compiles what you tell it to. Compilers are only smart for lower level optimizations and problems the programmer overlooks (such as computation inside a for loop, dead code etc).
What you tell the compiler to do in the second example, is to make a copy of the class - which it will do without thinking - even if you didn't use it, that's what you asked the compiler to do.
The second example explicitly asks the compiler to use the same variable - conserving space and precious cycles (no copy is needed). The const is there for mistakes - since
Class &value
can be written to (sometimes it's desired).