From ee3c65138d5c8142722cb2fc398df1cdedf76112 Mon Sep 17 00:00:00 2001 From: Marco Borgeaud <89914223+marco-antognini-sonarsource@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:03:05 +0100 Subject: [PATCH] Modify rule S5425: Update RSPEC to mention forward_like (CPP-5031) --- rules/S5425/cfamily/rule.adoc | 87 +++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/rules/S5425/cfamily/rule.adoc b/rules/S5425/cfamily/rule.adoc index 23ed8b0bd02..837a0dc5c79 100644 --- a/rules/S5425/cfamily/rule.adoc +++ b/rules/S5425/cfamily/rule.adoc @@ -1,56 +1,83 @@ == Why is this an issue? -_Forwarding references_ are a special kind of references that both ignore and preserve the _value category_ of a function argument, making it possible to forward it by means of ``++std::forward++``. +_Forwarding references_ are a special kind of references that both ignore and preserve the _value category_ of a function argument, making it possible to forward it by using ``++std::forward++`` or ``++std::forward_like++``. -Any code using such a reference for any other purpose than forwarding is actually ignoring rvalue-ness and const-ness of the associated parameter. +Any code using such a reference for any purpose other than forwarding is actually ignoring the rvalue-ness and const-ness of the associated parameter. === Noncompliant code example -[source,cpp] +[source,cpp,diff-id=1,diff-type=noncompliant] ---- -#include -#include -#include - -template void f( TP&& arg ) { - std::string s(arg); -} - -int main() { - std::string s("test"); - f(std::move(s)); - std::cout<<"f:"< names; + + public: + template + void addName(StringLike&& arg) { + names.emplace_back(arg); // Not forwarded + } +}; + +void example() { + Registry r; + + std::string name = "example"; + r.addName(std::move(name)); + std::cout << "name:" << name << std::endl; + // output is "name:example" } ---- +In this example, the intent is to move the content of `name` into the vector, but instead a copy is made. === Compliant solution -[source,cpp] +[source,cpp,diff-id=1,diff-type=compliant] ---- -#include -#include -#include - -template void f( TP&& arg ) { - std::string s(std::forward(arg)); +class Registry { + std::vector names; + + public: + template + void addName(StringLike&& arg) { + names.emplace_back(std::forward(arg)); + } +}; + +void example() { + Registry r; + + std::string name = "example"; + r.addName(std::move(name)); + std::cout << "name:" << name << std::endl; + // output can be anything: name has been moved-from } +---- -int main() { - std::string s("test"); - f(std::move(s)); - std::cout<<"f:"< + void addNames(PairOfStrings&& arg) { + addName(std::forward_like(arg.second)); // We don't care about arg.first + } +}; ---- == Resources +=== Documentation + +* {cpp} reference - https://en.cppreference.com/w/cpp/utility/forward[`std::forward`] +* {cpp} reference - https://en.cppreference.com/w/cpp/utility/forward_like[``++std::forward_like++``] + +=== External coding guidelines + * {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/e49158a/CppCoreGuidelines.md#f19-for-forward-parameters-pass-by-tp-and-only-stdforward-the-parameter[F.19: For "forward" parameters, pass by `TP&&` and only `std::forward` the parameter] ifdef::env-github,rspecator-view[]