Reputation: 2383
I'm reading the book C++ Primer Plus (6th Edition) and I've come across something that is kind of confusing to me, so bear with me while I try to explain...
If I have a function whose prototype looks like so:
void cube(double x);
and another function who prototype looks like so:
void cube(const double &x);
what is the difference between the two? For the first function prototype, the value is passed by value meaning that it will be copied and thus unaltered by the function. For the second prototype, the value is passed by reference but it's a constant reference so C++ will create an anonymous temporary variable and assign the value of the argument to the temporary variable thus mimicking pass by value. So, in essence there is really no difference between the two function prototypes, right? What is the point of (const double &x) then?
Upvotes: 3
Views: 3429
Reputation: 10400
Well, there is some difference:
void cube(double x)
, you're making a local copy of variable x in your function, and you can change its value without changing the value of the original x. So, in the local function void cube
, you can interact with x as with ordinal variable.void cube(const double& x)
, you're just passing a pointer (in c++, reference is a pointer but with slightly another syntax of usage), and const
here means that you can't change the value of the variable in this address (that the pointer is pointed on). So, in the local function void cube
, you should interact with x as with a constant variable.What about difference in performance?
With double
there's no difference in performance, because double
takes 64 bits, and the reference to double
takes 32 or 64 bits, not much difference. But, imagine you have a struct:
struct some_very_big_struct {
...
}
where sizeof(some_very_big_struct)
is 2^10
bytes, so making a copy of this struct takes really much time and additional memory, so in that case passing a reference is the best choice.
Upvotes: 1
Reputation: 726529
For the second prototype, the value is passed by reference but it's a constant reference so C++ will create an anonymous temporary variable and assign the value of the argument to the temporary variable thus mimicking pass by value.
Although C++ would do this in certain situations. For example, if you pass an expression returning double
, C++ will create a temporary:
double v = 123.456;
cube(5*v+321.0123);
However, it would not necessarily do that. For example, if you make this call
double v = 123.456;
cube(v);
C++ would pass a reference to v
directly to the cube()
function. In this case, concurrent modifications to v
would be "visible" to the cube()
function while it is running.
So, in essence there is really no difference between the two function prototypes, right?
That's right, there isn't much difference between the two. Assuming that double
takes the same amount of space as a reference to double
, there would be no difference in performance as well.
What is the point of
(const double &x)
then?
Although there is little difference between passing double by value or by constant reference, you may get considerable difference when dealing with other types, such as std::string
. Taking parameters by constant reference becomes very useful when you code an algorithm as a template, i.e. when you have to write
void cube(const T& v);
and your T
could be any type. Constant reference approach lets you control the amount of copying that is going on, because starting at a certain object size, passing a reference becomes much cheaper than passing a copy.
Upvotes: 4
Reputation: 16888
The standard clearly requires, subject to the rules in [dcl.init.ref] #5.1
, that the reference parameter to be bound to the argument, as opposed (just for example) to the reference to be bound to some copy. The difference is detectable, so no "as if" rule is applicable (in all cases).
double a;
void foo(const double &x) { assert(&a == &x); }
void bar() { foo(a); }
There is a practical difference too. In the case of const double &x
, the name x
is just an alias for some double that may be accessed via other lvalues, which has a lot of implications to optimisation (like register saves/restores, instruction reordering, etc), for example:
double a;
void foo(const double &x) {
... = x;
a = bar(); // here the compiler must assume that the assignment
// may modify x, the two statements can't be reordered
}
void foo(double x) {
... = x;
a = bar(); // here the compiler knows that the assignment
// cannot modify x, and the two statements can be
// reordered
}
Upvotes: 1
Reputation: 73
Although the read/write ops would slow it down, a reference/pointer would only be 32 bits on 32bit machine, where a double would be 64 bits. That doesn't play as well with a 32 bit word size.
It makes no sense, really. An optimising compiler will probably treat them the same and generate identical code.
EDIT: To explain myself, I should have emphasised "probably".
Strict standards compliance is currently a bad way to judge whether or not a compiler is indeed a C++ compiler - compilers often abuse/ignore the standard in favour of optimisations and syntactic sugar.
As for the (performance) difference, I was trying to say measuring the cost of passing a double (over the word size) to the function VS the cost of fetching from a pointer/reference (word size) - and in most cases it's negligible. The larger the value you're passing, the faster fetching from pointer/reference will become.
Casey Muratori mentioned this, and encourages passing structs slightly over the word size.
Upvotes: -2
Reputation: 27528
For primitive types, you don't gain anything by using a const&
. const&
is traditionally used for big objects to avoid possibly expensive and unnecessary copying.
You also need const&
if the type does not support copying yet you want to disallow modification of the referenced object.
With the advent of C++11, move semantics question the traditional wisdom of "pass small objects by value, big objects by const reference" As far as I can see, the C++ expert community has not yet reached a new consensus on this topic. For example, see How true is "Want Speed? Pass by value".
Upvotes: 1
Reputation: 4174
For the following two prototypes
void cube(double x);
and
void cube(const double &x);
the behavior will be the same from the perspective of the caller and the callee in that neither will allow modification to propagate to the caller. However, in the latter (const reference) example, x will be passed by reference instead of by value. If double were large enough, or if it was a larger data type (e.g., struct) passing by reference would avoid copying a lot of data (since only the pointer is passed).
The second-order performance effects (e.g., effects on cache weighed against the copy of the data) are all very implementation-dependent, and depend not only upon the processor / cache architecture and compiler, but on the structure and runtime use case of the program.
Upvotes: 4