For "forward" parameters, pass by `TP&&` and only `std::forward` the parameter
Reason
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 TP&& 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
Usually you forward the entire parameter (or parameter pack, using ...) exactly once on every static control flow path:
template<class F, class... Args>
inline decltype(auto) invoke(F&& f, Args&&... args)
{
return forward<F>(f)(forward<Args>(args)...);
}
Example
Sometimes you may forward a composite parameter piecewise, each subobject once on every static control flow path:
template<class PairLike>
inline auto test(PairLike&& pairlike)
{
// ...
f1(some, args, and, forward<PairLike>(pairlike).first); // forward .first
f2(and, forward<PairLike>(pairlike).second, in, another, call); // forward .second
}
Enforcement
- Flag a function that takes a
TP&¶meter (whereTPis a template type parameter name) and does anything with it other thanstd::forwarding it exactly once on every static path, orstd::forwarding it more than once but qualified with a different data member exactly once on every static path.