Functions
F.20
For "out" output values, prefer return values to output parameters
Reason
A return value is self-documenting, whereas an & could be either in-out or out-only and is liable to be misused.
This includes large objects like standard containers that use implicit move operations for performance and to avoid explicit memory management.
If you have multiple values to return, use a tuple or similar multi-member type.
Example
// OK: return pointers to elements with the value x
vector<const int*> find_all(const vector<int>&, int x);
// Bad: place pointers to elements with value x in-out
void find_all(const vector<int>&, vector<const int*>& out, int x);
Note
A struct of many (individually cheap-to-move) elements might be in aggregate expensive to move.
Exceptions
- For non-concrete types, such as types in an inheritance hierarchy, return the object by
unique_ptrorshared_ptr. - If a type is expensive to move (e.g.,
array<BigTrivial>), consider allocating it on the free store and return a handle (e.g.,unique_ptr), or passing it in a reference to non-consttarget object to fill (to be used as an out-parameter). - To reuse an object that carries capacity (e.g.,
std::string,std::vector) across multiple calls to the function in an inner loop: treat it as an in/out parameter and pass by reference.
Example
Assuming that Matrix has move operations (possibly by keeping its elements in a std::vector):
Matrix operator+(const Matrix& a, const Matrix& b)
{
Matrix res;
// ... fill res with the sum ...
return res;
}
Matrix x = m1 + m2; // move constructor
y = m3 + m3; // move assignment
Note
The return value optimization doesn't handle the assignment case, but the move assignment does.
Example
struct Package { // exceptional case: expensive-to-move object
char header[16];
char load[2024 - 16];
};
Package fill(); // Bad: large return value
void fill(Package&); // OK
int val(); // OK
void val(int&); // Bad: Is val reading its argument
Enforcement
- Flag reference to non-
constparameters that are not read before being written to and are a type that could be cheaply returned; they should be "out" return values.