I know I've posted this before... but here it goes:
Passing by value is good because it avoids having to have indirection in the function. Passing by reference or by pointer means that whenever you access the value, you must deference the pointer/ref. This is an extra memory access which = slower.
Passing by value is bad because it requires the object to be copied. In cases where object copies are computationally expensive, this is a big performance hit.
HOWEVER
The indirection might be unavoidable in some complex types. For example, member functions
always (or really 99% of the time) have indirection because a 'this' pointer is passed and implicitly dereferenced. So passing a copy of a std::string doesn't really avoid any indirection, because you have to access string through member functions, and those functions add indirection.
What's more: passing any kind of parameter involves a copy. If you're passing by reference/pointer, then a pointer is being copied (you're effectively passing a pointer by value). So passing an int by reference doesn't gain you anything, because you're still copying a pointer/ref and passing it by value -- which is just as expensive as copying the int -- and now you also have indirection because you have to dereference the pointer/ref to use it.
THEREFORE, the general rules of thumb are as follows:
Things to pass by value:
Basic types like int, bool, double, pointers, etc. They're cheap to copy, so don't bother with passing by reference.
Things to pass by const reference:
Complex types / classes like std::string, std::vector, or any other class where you interface with it through member functions / overloaded operators.
Things where you must use your judgement:
Data structures where you access data directly. For example something like this:
1 2 3 4 5
|
struct Point
{
int x;
int y;
};
|
These might be cheap to copy and it doesn't have any indirection to access it's data, so you might be better off passing by value. However as the struct grows larger, copying becomes more expensive so it turns into a tradeoff. The right decision depends on how it's used in the function, how often it's used, etc.
And of course remember that
there are always exceptions to the rules.