diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index c040357..bd6fba6 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1785,7 +1785,6 @@ Argument passing rules: * [F.17: Use a `not_null` to indicate "null" is not a valid value](#Rf-nullptr) * [F.18: Use a `span` or a `span_p` to designate a half-open sequence](#Rf-range) * [F.19: Use a `zstring` or a `not_null` to designate a C-style string](#Rf-string) -* [F.24: Use a `TP&&` parameter when forwarding (only)](#Rf-pass-ref-ref) * [F.25: Use a `T&&` parameter together with `move` for rare optimization opportunities](#Rf-pass-ref-move) * [F.26: Use a `unique_ptr` to transfer ownership where a pointer is needed](#Rf-unique_ptr) * [F.27: Use a `shared_ptr` to share ownership](#Rf-shared_ptr) @@ -2181,9 +2180,9 @@ Pure functions are easier to reason about, sometimes easier to optimize (and eve Not possible. -## F.call: Argument passing +## F.call: Parameter passing -There are a variety of ways to pass arguments to a function and to return values. +There are a variety of ways to pass parameters to a function and to return values. ### Rule F.15: Prefer simple and conventional ways of passing information @@ -2314,12 +2313,24 @@ A reference may 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." + +**For an "forwarded" value:** If the object is to be passed onward to other code and not directly used by this function, we want to make this function agnostic to the argument `const`-ness and rvalue-ness. In that case, and only that case, make the parameter `TP&&` where `TP` is a template type parameter -- it both *ignores* and *preserves* `const`-ness and rvalue-ness. Therefore any code that uses a `T&&` is implicitly declaring that it itself doesn't care about the variable's `const`'-ness and rvalue-ness (because it is ignored), but that intends to pass the value onward to other code that does care about `const`-ness and rvalue-ness (because it is preserved). When used as a parameter `TP&&` is safe because any temporary objects passed from the caller will live for the duration of the function call. A parameter of type `TP&&` should essentially always be passed onward via `std::forward` in the body of the function. + +##### Example + + template + inline auto invoke(F&& f, Args&&... args) { + return forward(f)(forward(args)...); + } + + ##### Enforcement * (Simple) ((Foundation)) Warn when a parameter being passed by value has a size greater than `4 * sizeof(int)`. Suggest using a `const` reference instead. * (Simple) ((Foundation)) Warn when a `const` parameter being passed by reference has a size less than `3 * sizeof(int)`. Suggest passing by value instead. * (Moderate) ((Foundation)) Warn about functions with non-`const` reference arguments that do *not* write to them. +* Flag a function that takes a `TP&&` parameter (where `TP` is a template type parameter name) and uses it without `std::forward`. **See also**: [implicit arguments](#Ri-explicit). @@ -2458,23 +2469,6 @@ When I call `length(s)` should I test for `s == nullptr` first? Should the imple **See also**: [Support library](#S-gsl). -### F.24: Use a `TP&&` parameter when forwarding (only) - -##### Reason - -When `TP` is a template type parameter, `TP&&` is a forwarding reference -- it both *ignores* and *preserves* `const`'ness and rvalue-ness. Therefore any code that uses a `T&&` is implicitly declaring that it itself doesn't care about the variable's `const`'-ness and rvalue-ness (because it is ignored), but that intends to pass the value onward to other code that does care about `const`'-ness and rvalue-ness (because it is preserved). When used as a parameter `TP&&` is safe because any temporary objects passed from the caller will live for the duration of the function call. A parameter of type `TP&&` should essentially always be passed onward via `std::forward` in the body of the function. - -##### Example - - template - inline auto invoke(F&& f, Args&&... args) { - return forward(f)(forward(args)...); - } - -##### Enforcement - -Flag a function that takes a `TP&&` parameter (where `TP` is a template type parameter name) and uses it without `std::forward`. - ### F.25: Use a `T&&` parameter together with `move` for rare optimization opportunities ##### Reason