Home > Net >  Why should I use pass by value over pass by reference?
Why should I use pass by value over pass by reference?

Time:12-15

I was studying about advanced topics, and came across a question in my mind - why would I need to use pass-by-value instead of pass-by-reference?

The question is originated around memory management, which is, if passing a variable as a value into a function makes a copy of the variable, shouldn't I need to pass it by reference as const to efficiently use memory?

When I researched on Google, it generally stated that pass-by-value should be used when we don't want to change the variable inside of a function.

Here is my example below:

#include <iostream>

void foo(int a) {
   std::cout << "value of a: " << a << std::endl;
}

int main(){
   int b = 5;
   foo(5);
   return EXIT_SUCCESS;
}

In the code block above, I passed b as value into the function foo(). Which made a copy of b.

Instead, if I write the function like below:

#include <iostream>

void foo(const int& a) {
   std::cout << "value of a: " << a << std::endl;
}

int main(){
   int b = 5;
   foo(5);
   return EXIT_SUCCESS;
}

With this code block, I don't make a copy of b.

What are the advantages of using pass-by-value when I am calling functions instead of pass-by-reference, besides when I don't want to change the content of the variable inside of a function?

CodePudding user response:

For larger or more complex custom types, making a full and deep copy is not a trivial task. A classic example might be an array containing 1 million elements. If we pass it into the function by value, at the point of the call, all 1 million elements in that array need to be copied so that you can use them in the function. Most of the time, such a copy would be unnecessary and we can save a lot of processing by simply passing by reference.

I'll also add the note that if passing by reference, it is almost always best to pass by const reference unless you explicitly intend to modify the variable inside the function. This is for several reasons, three of which are:

  • It signals to everyone else that you don't want to modify it, and forbids you from doing so accidentally.
  • const variables can only be passed by const reference, so it allows your function to use both const and mutable (non-const) parameters.
  • rvalue references can bind to const lvalue references but not mutable ones, meaning that functions with const (lvalue) reference parameters can take temporaries and literals as parameters.

As is usually the case with C , there are exceptions. Fundamental types like int, double, bool, etc are optimised to be passed by value on most modern systems, meaning that passing them by reference unnecessarily can be a performance downgrade, not upgrade. The rule of thumb I recommend for them is to only pass by reference if you have a specific extraneous reason for doing so.

I'll also add the caveat that passing by reference is to avoid unnecessary copies. There are situations out there where making a copy or passing by value is the best call and what makes the most sense. In those situations, don't be zealous and try to cheat your code into passing by reference if it really should be passing by value.

CodePudding user response:

Passing integers by value is usually better for both performance and codesize.

The reason is that passing arguments usually happens through registers, which are faster than memory. If you pass a pointer, then unless the compiler inlines (which it often can't do and often won't do because it isn't a good idea), you effectively force the compiler to spill into memory what probably was held in a register. Those memory accesses will cost and code will need to be generated to do the spill and to load the address.

The difference isn't that great. I'm measuring it at about 0.3 ns per call in favor of passing integers by value (I got calls taking ints by value taking 1.5ns and calls taking ints by pointer/ref at 1.8ns). For larger objects that wouldn't be passed through registers (on Linux's x86-64 SysV that means for objects larger than 2 longs), passing by pointer/reference starts being a big win and the bigger, the larger the object.

CodePudding user response:

As you know already, by reference parameters save you a copy, which is an advantage unless, as mentioned in some comments, you pass by reference a plain old data type. As soon as you are not passing ints/doubles/chars/Foo*s etc, you want to pass by reference:

  • most likely by const&, because if you were "happy" with passing by value, then you were not trying to modify the argument at the call site in the first place, so with const& you're ensuring that still holds, and you save the copy;

  • or & if you really want to modify the argument at the call site, but you're already doing that where you needed, because pass-by-value didn't let you do what you needed;

  • and then there's && (rvalue reference, not forwarding reference) for when you want to avoid the copy of the argument but forcing the caller to pass you a temporary (or a thing they are happy to hand over to you via std::move) that you can mess up with.

  • Forwarding references, T&& with T being a template parameter, are used too, but for that I would recommend reading this.

So, in any case, you save a copy.


And that is only one advantage. Another advantage is that you can't pass non-copyable classes by copy.¹


So no, unless you're talking of plain old data types, you really never prefer pass-by-value over pass-by-reference.


(¹) Despite std::unique_ptr is a non-copyable class, it doesn't really represent an example of what you'd prefer to pass by reference. What you'd want to do is to move its value into the callee (to say "dear callee, caller speaking: you own this thing now, not me"); in other words, the caller would std::move the std::unique_ptr<T> and the callee would take it by std::unique_ptr<T>, i.e. by value.

  •  Tags:  
  • c
  • Related