For "in" parameters, pass cheaply-copied types by value and others by reference to `const`
Reason
Both let the caller know that a function will not modify the argument, and both allow initialization by rvalues.
What is "cheap to copy" depends on the machine architecture, but two or three words (doubles, pointers, references) are usually best passed by value. When copying is cheap, nothing beats the simplicity and safety of copying, and for small objects (up to two or three words) it is also faster than passing by reference because it does not require an extra indirection to access from the function.
Example
void f1(const string& s); // OK: pass by reference to const; always cheap
void f2(string s); // bad: potentially expensive
void f3(int x); // OK: Unbeatable
void f4(const int& x); // bad: overhead on access in f4()
For advanced uses (only), where you really need to optimize for rvalues passed to "input-only" parameters:
add an overload that passes the parameter by && (for rvalues) and in the body std::moves it to its destination. Essentially this overloads a "will-move-from"; see F.18.
- If the function is going to unconditionally move from the argument, take it by
&&. See F.18. - If the function is going to keep a locally modifiable copy of the argument only for its own local use, taking it by value is fine.
- If the function is going to keep a copy of the argument to pass to another destination (to another function, or store in a non-local location), in addition to passing by
const&(for lvalues), - In special cases, such as multiple "input + copy" parameters, consider using perfect forwarding. See F.19.
Example
int multiply(int, int); // just input ints, pass by value
// suffix is input-only but not as cheap as an int, pass by const&
string& concatenate(string&, const string& suffix);
void sink(unique_ptr<widget>); // input only, and moves ownership of the widget
Avoid "esoteric techniques" such as passing arguments as T&& "for efficiency". Most rumors about performance advantages from passing by && are false or brittle (but see F.18 and F.19).
Notes
A reference can be assumed to refer to a valid object (language rule). There is no (legitimate) "null reference." If you need the notion of an optional value, use a pointer, std::optional, or a special value used to denote "no value."
Enforcement
Suggest using a reference to const instead.
- (Simple) ((Foundation)) Warn when a parameter being passed by value has a size greater than
4 * sizeof(void*). - (Simple) ((Foundation)) Warn when a parameter passed by reference to
consthas a size less or equal than2 * sizeof(void*). Suggest passing by value instead. - (Simple) ((Foundation)) Warn when a parameter passed by reference to
constismoved. - (Not simple) Note: A stricter enforcement would depend on the performance characteristics of the target architecture.
Exception
To express shared ownership using shared_ptr types, follow R.34 or R.36, depending on whether or not the function unconditionally takes a reference to the argument.