* [F.18: For "will-move-from" parameters, pass by `X&&` and `std::move` the parameter](#Rf-consume)
* [F.19: For "forward" parameters, pass by `TP&&` and only `std::forward` the parameter](#Rf-forward)
* [F.20: For "out" output values, prefer return values to output parameters](#Rf-out)
* [F.21: To return multiple "out" values, prefer returning a struct or tuple](#Rf-out-multi)
* [F.21: To return multiple "out" values, prefer returning a struct](#Rf-out-multi)
* [F.60: Prefer `T*` over `T&` when "no argument" is a valid option](#Rf-ptr-ref)
Parameter passing semantic rules:
@ -3228,13 +3228,15 @@ The return value optimization doesn't handle the assignment case, but the move a
* Flag reference to non-`const` parameters that are not read before being written to and are a type that could be cheaply returned; they should be "out" return values.
### <aname="Rf-out-multi"></a>F.21: To return multiple "out" values, prefer returning a struct or tuple
### <aname="Rf-out-multi"></a>F.21: To return multiple "out" values, prefer returning a struct
##### Reason
A return value is self-documenting as an "output-only" value.
Note that C++ does have multiple return values, by convention of using a `tuple` (including `pair`), possibly with the extra convenience of `tie` or structured bindings (C++17) at the call site.
Prefer using a named struct where there are semantics to the returned value. Otherwise, a nameless `tuple` is useful in generic code.
Note that C++ does have multiple return values, by convention of using tuple-like types (`struct`, `array`, `tuple`, etc.),
possibly with the extra convenience of structured bindings (C++17) at the call site.
Prefer using a named `struct` if possible.
Otherwise, a `tuple` is useful in variadic templates.
##### Example
@ -3247,30 +3249,29 @@ Prefer using a named struct where there are semantics to the returned value. Oth
}
// GOOD: self-documenting
tuple<int,string> f(const string& input)
struct f_result { int status; string data; };
f_result f(const string& input)
{
// ...
return {status, something()};
}
C++98's standard library already used this style, because a `pair` is like a two-element `tuple`.
C++98's standard library used this style in places, by returning `pair` in some functions.
For example, given a `set<string> my_set`, consider:
// C++98
result = my_set.insert("Hello");
if (result.second) do_something_with(result.first); // workaround
With C++11 we can write this, putting the results directly in existing local variables:
pair<set::iterator,bool> result = my_set.insert("Hello");
if (result.second)
do_something_with(result.first); // workaround
Sometype iter; // default initialize if we haven't already
Someothertype success; // used these variables for some other purpose
With C++17 we are able to use "structured bindings" to give each member a name:
tie(iter, success) = my_set.insert("Hello"); // normal return value
if (success) do_something_with(iter);
if (auto [ iter, success ] = my_set.insert("Hello"); success)
do_something_with(iter);
With C++17 we are able to use "structured bindings" to declare and initialize the multiple variables:
if (auto [ iter, success ] = my_set.insert("Hello"); success) do_something_with(iter);
A `struct` with meaningful names is more common in modern C++.
See for example `ranges::min_max_result`, `from_chars_result`, and others.
##### Exception
@ -3292,17 +3293,19 @@ By reusing `s` (passed by reference), we allocate new memory only when we need t
This technique is sometimes called the "caller-allocated out" pattern and is particularly useful for types,
such as `string` and `vector`, that needs to do free store allocations.
To compare, if we passed out all values as return values, we would something like this:
To compare, if we passed out all values as return values, we would write something like this: