-2

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?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
E. Tolga
  • 41
  • 4
  • 4
    "When I made a research on google it generally stated that the pass by value should used when we don't want to change variable inside a function" where did you find that? I never heard about that. There is a recommendation to pass types that arent bigger than a pointer by value, but generally suggesting to pass by value is just completely off. – 463035818_is_not_an_ai Dec 14 '22 at 12:28
  • 1
    Can you give a link to the research you've found? – xiaoyu2006 Dec 14 '22 at 12:30
  • 2
    the advantage of pass by reference is that no copy is made. The information you found about "always make a copy" is wrong. We can only explain what you read if you post a link to where you read that. – 463035818_is_not_an_ai Dec 14 '22 at 12:30
  • 2
    my advice is to not think too much in terms of efficiency, but in terms of what you want the code to do. Do you want it to make a copy? Then make a copy. Most of the time you do not want to make a copy. – 463035818_is_not_an_ai Dec 14 '22 at 12:36
  • [site 1](https://courses.washington.edu/css342/zander/css332/passby.html#:~:text=Use%20pass%20by%20value%20when,the%20actual%20parameter%20is%20stored.) [site 2](https://blog.penjee.com/passing-by-value-vs-by-reference-java-graphical/) [site 3](https://www.educative.io/answers/pass-by-value-vs-pass-by-reference) Here are the few sites that looked in. – E. Tolga Dec 14 '22 at 12:39
  • 1
    the second link is not about C++, in the others I didnt find that they would generally recommend to pass by value. Can you quote the part you refer to? – 463035818_is_not_an_ai Dec 14 '22 at 12:45
  • 2
    @E.Tolga The first link you gave quite explicitly says don't use pass by value when you have a large object (because of the cost of copying). The third link also makes the same point. – john Dec 14 '22 at 12:48
  • 1
    Note pass by reference is NOT an advanced topic, it is core to what C++ is all about. In this case passing a value that's equal in size or smaller then a pointer size, pass by value is more efficient. – Pepijn Kramer Dec 14 '22 at 12:59
  • If your type is smaller than a pointer type or if you need a copy to work on inside the function, pass by value, otherwise, pass by const reference. – Fareanor Dec 14 '22 at 13:16
  • IMHO, pass by value when the data type can fit into the processor's register, otherwise pass by reference or constant reference. – Thomas Matthews Dec 14 '22 at 18:24

3 Answers3

6

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.

mooproxy
  • 71
  • 2
  • An example of the last case is when you wish to assign to a parameter within the function. In this case you either have to pass by value or you have to copy to a local variable from your parameter. Passing by value in this case makes sense to me. – john Dec 14 '22 at 12:56
  • please note that OP is asking for advantages of pass by value, not about advantages of pass by reference. – 463035818_is_not_an_ai Dec 14 '22 at 12:58
3

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.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
2

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.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • note that the quesiton is "why of pass by value better?". OP knows that one makes a copy the other not, but read somewhere that a copy should be prefered. – 463035818_is_not_an_ai Dec 14 '22 at 12:37
  • Perhaps this answer would be more complete with a `Foo(Foo&&)`? – Bathsheba Dec 14 '22 at 12:39
  • @Bathsheba, I'm not sure. The question is about pass-by-value vs pass-by-reference of arguments to functions. The fact that that `Foo` is movable or not has no influence on what happens when a function accepts `Foo [const|&]&`, because no copy nor move happens anyway. It does influence what happens when a function accepts `Foo` and is fed with an rvalue vs lvalue, yes, but I'm not sure I've got why you think this would make the answer more complete. – Enlico Dec 14 '22 at 12:44
  • @463035818_is_not_a_number it wasn't preferred as copy should be used. My actual question why would I choose pass by value instead of reference when it only makes the copy so that the value do not changed in the function where I can pass it by reference. By the way, I don't include computational side of it. I know it should be passed by value or reference as what it needed at that moment to make computations using the variable. – E. Tolga Dec 14 '22 at 12:45
  • 1
    `Another advantage is that you can't pass by copy non-copyable classes, such as std::unique_ptr.`. This is a bad example.Passing a `std::unique_ptr` by const ref is almost always the wrong thing to do. Either give ownership to the function (`std::move` it in) or pass in a `T const&` The callee almost never cares how you are storing a value. – Mike Vine Dec 14 '22 at 12:48
  • @E.Tolga " copy should be used." no generally it *should* not. "My actual question why would I choose pass by value instead of reference ..." you would not. I am still trying to find out why you think so. – 463035818_is_not_an_ai Dec 14 '22 at 12:50
  • @MikeVine, you're right, `std::unique_ptr` is a bad example. I'll clarify. – Enlico Dec 14 '22 at 12:57