views:

277

answers:

5

This might be a stupid question, but I notice that in a good number of APIs, a lot of method signatures that take integer parameters that aren't intended to be modified look like:

void method(int x);

rather than:

void method(const int &x);

To me, it looks like both of these would function exactly the same. (EDIT: apparently not in some cases, see answer by R Samuel Klatchko) In the former, the value is copied and thus can't change the original. In the latter, a constant reference is passed, so the original can't be changed.

What I want to know is why one over the other - is it because the performance is basically the same or even better with the former? e.g. passing a 16-bit value or 32-bit value rather than a 32-bit or 64-bit address? This was the only logical reason I could think of, I just want to know if this is correct, and if not, why and when you would prefer int x over const int &x and/or vice versa. Thanks.

+12  A: 

It's not just the cost of passing a pointer (that's essentially what a reference is), but also the de-referencing in the called method's body to retrieve the underlying value.

That's why passing an int by value will be virtually guaranteed to be faster (Also, the compiler can optimize and simply pass the int via processor registers, eliminating the need to push it onto the stack).

Jen
+2  A: 

Integers are usually the size of the processor's native word and can pass easily into a registers. From this perspective, there is no difference between passing by value or passing by constant reference.

When in doubt, print the assembly language listing for your functions to find out how the compiler is passing the argument. Print out for both pass by value and pass by constant reference.

Also, when passing by value, the function can modify the copy. When passing by constant reference, the function cannot modify the variable (it's marked as const).

Thomas Matthews
A: 

There will probably be a very, very small de-optimization for passing by reference, since at the very least one dereference will need to occur to get the actual value (unless the call is inlined, the compiler cannot simply pass the value due to the fact that the call site and function might be separately compiled, and it's valid and well-defined to cast away the const for a passed parameter that isn't actually const itself - see http://stackoverflow.com/questions/1727569/what-are-the-benefits-to-passing-integral-types-by-const-ref/1727606#1727606). Note, however, that the 'de-optimization' is likely to be so small as to be difficult to measure.

Most people seem to dislike pass-by-const-ref for built-ins because of this (some very much). However, I think that it it might be preferable in some cases if you want the compiler to assist you in ensuring that the value isn't accidentally changed within the function. It's not a big thing, but sometimes it might help.

Michael Burr
Good point on the accidental change prevention, although I don't exactly see why this would be useful since you'd only be modifying a copy otherwise.
Jake Petroules
pass by value assures the original not to be changed just as well as pass-by-const-ref.
xtofl
And the de-optimization is likely to become a problem when you talk about huge data volumes (e.g. dsp processing)
xtofl
If you're worried about accidentally changing it in the body, even though that doesn't affect the caller, just declare it as a const value
Chris Dodd
A couple points - the const simply helps prevent accidents like many other uses of const (and could just as well be applied to the pass-by-value version of the parameter, as Chris Dodd mentioned). My main point (which on second reading is pretty obfuscated) is that it really doesn't matter in 99% of the cases; where it does matter, by all means pass-by-value. But there are probably other, more pressing things to worry about. Like brace placement or whether members should have `m_` preceding the name.
Michael Burr
+3  A: 
  • Depending on the underlying instruction set, an integer parameter can be passed as register or on the stack. Register is definitely faster than memory access, which would always be required in case of const refs (considering early cache-less architectures)

  • You cannot pass an int literal as a const int&

  • Explicit type-casts allow you cast a const int& into * (const int *) opening the possibility to change the value of the passed reference

devio
You absolutely can pass an integer literal as a const reference. Const references can take rvalues, including literals of any sort. I have no idea what you mean by your third point; C++ will let you cast an integer into an elephant if you want to.
Tyler McHenry
@Tyler McHenry: 3rd point became victim of markup. fixed
devio
+11  A: 

To me, it looks like both of these would function exactly the same.

It depends on exactly what the reference is to. Here is an admittedly made up example that would change based on whether you pass a reference or a value:

static int global_value = 0;

int doit(int x)
{
    ++global_value;
    return x + 1;
}

int main()
{
    return doit(global_value);
}

This code will behave differently depending on whether you have int doit(int) or int doit(const int &)

R Samuel Klatchko
Tricky... I never would have thought of a case like this, thanks for posting it.
Jake Petroules
There's also the risk that the function might keep a non-const reference to the value, rather than a copy.
Adrian McCarthy