Is it better in C++ to pass by value or pass by reference-to-const?
Asked Answered
M

11

241

Is it better in C++ to pass by value or pass by reference-to-const?

I am wondering which is better practice. I realize that pass by reference-to-const should provide for better performance in the program because you are not making a copy of the variable.

Martino answered 6/11, 2008 at 21:43 Comment(1)
related: #2139724Pelite
A
236

It used to be generally recommended best practice1 to use pass by const ref for all types, except for builtin types (char, int, double, etc.), for iterators and for function objects (lambdas, classes deriving from std::*_function).

This was especially true before the existence of move semantics. The reason is simple: if you passed by value, a copy of the object had to be made and, except for very small objects, this is always more expensive than passing a reference.

With C++11, we have gained move semantics. In a nutshell, move semantics permit that, in some cases, an object can be passed “by value” without copying it. In particular, this is the case when the object that you are passing is an rvalue.

In itself, moving an object is still at least as expensive as passing by reference. However, in many cases a function will internally copy an object anyway — i.e. it will take ownership of the argument.2

In these situations we have the following (simplified) trade-off:

  1. We can pass the object by reference, then copy internally.
  2. We can pass the object by value.

“Pass by value” still causes the object to be copied, unless the object is an rvalue. In the case of an rvalue, the object can be moved instead, so that the second case is suddenly no longer “copy, then move” but “move, then (potentially) move again”.

For large objects that implement proper move constructors (such as vectors, strings …), the second case is then vastly more efficient than the first. Therefore, it is recommended to use pass by value if the function takes ownership of the argument, and if the object type supports efficient moving.


A historical note:

In fact, any modern compiler should be able to figure out when passing by value is expensive, and implicitly convert the call to use a const ref if possible.

In theory. In practice, compilers can’t always change this without breaking the function’s binary interface. In some special cases (when the function is inlined) the copy will actually be elided if the compiler can figure out that the original object won’t be changed through the actions in the function.

But in general the compiler can’t determine this, and the advent of move semantics in C++ has made this optimisation much less relevant.


1 E.g. in Scott Meyers, Effective C++.

2 This is especially often true for object constructors, which may take arguments and store them internally to be part of the constructed object’s state.

Anecdotal answered 6/11, 2008 at 21:49 Comment(8)
hmmm... I am not sure that it is worth to pass by ref. double-sBarbe
As usual, boost helps here. boost.org/doc/libs/1_37_0/libs/utility/call_traits.htm has template stuff to automatically figure out when a type is a builtin type (useful for templates, where you sometimes cannot know that easily).Bosson
This answer misses an important point. To avoid slicing, you must pass by reference (const or otherwise). See #275126Rattlepate
@Chris: right. I left the whole part of polymorphism out because that's a completely different semantics. I believe the OP (semantically) meant “by value” argument passing. When other semantics are required, the question doesn't even pose itself.Anecdotal
How would a function take ownership of the argument if it's passed by value? It would need to be passed by rvalue referenceEvanesce
@Evanesce In the context of this paragraph, “take ownership” doesn’t mean taking ownership away from another name, but merely taking it on, i.e. owning the resource afterwards. The important point isn’t whether (a copy of) the resource is owned somewhere else but merely whether the function also needs ownership. Of course you’re right that pass-by-value logically creates a copy. But if the argument is a temporary, no actual copy is made; instead, the value is moved. And if the value is not a temporary, rvalue references couldn’t be used anyway (unless the caller uses std::move).Anecdotal
meaning that whenever we pass by reference, it must be reference to const unless you explicitly want to modify the argument?Asymptomatic
@experimentunit1998X Yes, definitely. In fact, passing by non-const reference should be very rare in modern C++: most functions should not modify their arguments directly but instead return the modified value as a copy. This generally leads to safer and more maintainable code (of course there are exceptions for performance reasons).Anecdotal
B
104

Edit: New article by Dave Abrahams on cpp-next:

Want speed? Pass by value.


Pass by value for structs where the copying is cheap has the additional advantage that the compiler may assume that the objects don't alias (are not the same objects). Using pass-by-reference the compiler cannot assume that always. Simple example:

foo * f;

void bar(foo g) {
    g.i = 10;
    f->i = 2;
    g.i += 5;
}

the compiler can optimize it into

g.i = 15;
f->i = 2;

since it knows that f and g doesn't share the same location. if g was a reference (foo &), the compiler couldn't have assumed that. since g.i could then be aliased by f->i and have to have a value of 7. so the compiler would have to re-fetch the new value of g.i from memory.

For more pratical rules, here is a good set of rules found in Move Constructors article (highly recommended reading).

  • If the function intends to change the argument as a side effect, take it by non-const reference.
  • If the function doesn't modify its argument and the argument is of primitive type, take it by value.
  • Otherwise take it by const reference, except in the following cases
    • If the function would then need to make a copy of the const reference anyway, take it by value.

"Primitive" above means basically small data types that are a few bytes long and aren't polymorphic (iterators, function objects, etc...) or expensive to copy. In that paper, there is one other rule. The idea is that sometimes one wants to make a copy (in case the argument can't be modified), and sometimes one doesn't want (in case one wants to use the argument itself in the function if the argument was a temporary anyway, for example). The paper explains in detail how that can be done. In C++1x that technique can be used natively with language support. Until then, i would go with the above rules.

Examples: To make a string uppercase and return the uppercase version, one should always pass by value: One has to take a copy of it anyway (one couldn't change the const reference directly) - so better make it as transparent as possible to the caller and make that copy early so that the caller can optimize as much as possible - as detailed in that paper:

my::string uppercase(my::string s) { /* change s and return it */ }

However, if you don't need to change the parameter anyway, take it by reference to const:

bool all_uppercase(my::string const& s) { 
    /* check to see whether any character is uppercase */
}

However, if you the purpose of the parameter is to write something into the argument, then pass it by non-const reference

bool try_parse(T text, my::string &out) {
    /* try to parse, write result into out */
}
Buddha answered 7/11, 2008 at 5:52 Comment(5)
i found your rules good but im not to sure about the first part where u talk about not passing it as a ref would speed it up. yeah sure, but not passing something as a ref just cus of optimaztion doesnt make sense at all. if you want to change the stack object you are passing in, do so by ref. if you dont, pass it by value. if you dont wanna change it, pass it as const-ref. the optimization that comes with pass-by-value should not matter since you gain other things when passing as ref. i dont understand the "want speed?" sice if you where gonna perform those op you would pass by value anyway..Newlywed
Johannes: I loved that article when I read it, but I was disappointed when I tried it. This code failed on both GCC and MSVC. Did I miss something, or does it not work in practice?Lajuanalake
I don't think I agree that if you want to make a copy anyway, you'd pass it by value (instead of const ref), then move it. Look at it this way, what's more efficient, a copy and a move (you can even have 2 copies if you pass it forward), or just a copy? Yes there are some special cases to either side, but if your data cannot be moved anyway (ex: a POD with tons of integers), no need for extra copies.Greene
Mehrdad, not sure what you expected, but the code works as expectedGreene
I'd consider the necessity of copying just to convince the compiler that the types don't overlap a deficiency in the language. I'd rather use GCC's __restrict__ (which can also work on references) than do excessive copies. Too bad standard C++ hasn't adopted C99's restrict keyword.Botryoidal
S
16

Depends on the type. You are adding the small overhead of having to make a reference and dereference. For types with a size equal or smaller than pointers that are using the default copy ctor, it would probably be faster to pass by value.

Springbok answered 6/11, 2008 at 21:47 Comment(1)
For non-native types, you might (depending on how well the compiler optimises code) get a performance increase using const references instead of just references.Exosmosis
D
9

As it has been pointed out, it depends on the type. For built-in data types, it is best to pass by value. Even some very small structures, such as a pair of ints can perform better by passing by value.

Here is an example, assume you have an integer value and you want pass it to another routine. If that value has been optimized to be stored in a register, then if you want to pass it be reference, it first must be stored in memory and then a pointer to that memory placed on the stack to perform the call. If it was being passed by value, all that is required is the register pushed onto the stack. (The details are a bit more complicated than that given different calling systems and CPUs).

If you are doing template programming, you are usually forced to always pass by const ref since you don't know the types being passed in. Passing penalties for passing something bad by value are much worse than the penalties of passing a built-in type by const ref.

Demy answered 6/11, 2008 at 21:56 Comment(0)
C
9

This is what i normally work by when designing the interface of a non-template function:

  1. Pass by value if the function does not want to modify the parameter and the value is cheap to copy (int, double, float, char, bool, etc... Notice that std::string, std::vector, and the rest of the containers in the standard library are NOT)

  2. Pass by const pointer if the value is expensive to copy and the function does not want to modify the value pointed to and NULL is a value that the function handles.

  3. Pass by non-const pointer if the value is expensive to copy and the function wants to modify the value pointed to and NULL is a value that the function handles.

  4. Pass by const reference when the value is expensive to copy and the function does not want to modify the value referred to and NULL would not be a valid value if a pointer was used instead.

  5. Pass by non-const reference when the value is expensive to copy and the function wants to modify the value referred to and NULL would not be a valid value if a pointer was used instead.

Cholecyst answered 11/6, 2015 at 7:2 Comment(1)
Add std::optional to the picture and you no longer need pointers.Couperin
P
5

Sounds like you got your answer. Passing by value is expensive, but gives you a copy to work with if you need it.

Pickerelweed answered 6/11, 2008 at 21:44 Comment(4)
I'm not sure why this was voted down? It makes sense to me. If you are going to need the value currently stored, then pass by value. If not, pass the reference.Gualterio
It is totally type dependent. Doing a POD (plain old data) type by reference can in fact reduce performance by causing more memory accesses.Demy
Obviously passing int by reference doesn't save anything! I think the question implies things that are bigger than a pointer.Pickerelweed
It isn't that obvious, I've seen a lot of code by people who don't truly understand how computers work passing simple things by const ref because they have been told that is the best thing to do.Demy
B
4

As a rule passing by const reference is better. But if you need to modify you function argument locally you should better use passing by value. For some basic types the performance in general the same both for passing by value and by reference. Actually reference internally represented by pointer, that is why you can expect for instance that for pointer both passing are the same in terms of performance, or even passing by value can be faster because of needless dereference.

Barbe answered 6/11, 2008 at 21:50 Comment(2)
If you need to modify the callee's copy of the parameter, you could make a copy in the called code rather than passing by value. IMO you generally shouldn't choose the API based on an implementation detail like that: the calling code's source is the same either way, but its object code isn't.Capuchin
If you pass by value copy is created. And IMO there is no matter in which way you create a copy: via argument passing by value or locally - this is what concerns the C++. But from the design point of view I agree with you. But I describe C++ features here only and don't touch design.Barbe
D
4

Pass by value for small types.

Pass by const references for big types (the definition of big can vary between machines) BUT, in C++11, pass by value if you are going to consume the data, since you can exploit move semantics. For example:

class Person {
 public:
  Person(std::string name) : name_(std::move(name)) {}
 private:
  std::string name_;
};

Now the calling code would do:

Person p(std::string("Albert"));

And only one object would be created and moved directly into member name_ in class Person. If you pass by const reference, a copy will have to be made for putting it into name_.

Dingo answered 29/10, 2013 at 13:48 Comment(0)
D
1

As a rule of thumb, value for non-class types and const reference for classes. If a class is really small it's probably better to pass by value, but the difference is minimal. What you really want to avoid is passing some gigantic class by value and having it all duplicated - this will make a huge difference if you're passing, say, a std::vector with quite a few elements in it.

Decamp answered 6/11, 2008 at 22:4 Comment(2)
My understanding is that std::vector actually allocates its items on the heap and the vector object itself never grows. Oh, wait. If the operation causes a copy of the vector to be made, though, it will in fact go and duplicate all elements. That'd be bad.Deuteronomy
Yes, that's what I was thinking. sizeof(std::vector<int>) is constant, but passing it by value will still copy the contents in the absence of any compiler cleverness.Decamp
G
-5

Pass by referece is better than pass by value. I was solving the longest common subsequence problem on Leetcode. It was showing TLE for pass by value but accepted the code for pass by reference. Took me 30 mins to figure this out.

Gratify answered 24/4, 2021 at 8:39 Comment(0)
I
-7

Simple difference :- In function we have input and output parameter , so if your passing input and out parameter is same then use call by reference else if input and output parameter are different then better to use call by value .

example void amount(int account , int deposit , int total )

input parameter : account , deposit output paramteter: total

input and out is different use call by vaule

  1. void amount(int total , int deposit )

input total deposit output total

Innerve answered 5/1, 2015 at 4:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.