Skip to content

Commit

Permalink
Modify rule S5425: Update RSPEC to mention forward_like (CPP-5031)
Browse files Browse the repository at this point in the history
  • Loading branch information
marco-antognini-sonarsource committed Mar 27, 2024
1 parent b46c381 commit ee3c651
Showing 1 changed file with 57 additions and 30 deletions.
87 changes: 57 additions & 30 deletions rules/S5425/cfamily/rule.adoc
Original file line number Diff line number Diff line change
@@ -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 <utility>
#include <string>
#include <iostream>
template<typename TP> void f( TP&& arg ) {
std::string s(arg);
}
int main() {
std::string s("test");
f(std::move(s));
std::cout<<"f:"<<s<<std::endl; // output is "f:test"
return 0;
class Registry {
std::vector<std::string> names;
public:
template <typename StringLike>
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 <utility>
#include <string>
#include <iostream>
template<typename TP> void f( TP&& arg ) {
std::string s(std::forward<TP>(arg));
class Registry {
std::vector<std::string> names;
public:
template <typename StringLike>
void addName(StringLike&& arg) {
names.emplace_back(std::forward<StringLike>(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:"<<s<<std::endl; // output is "f:"
``++std::forward_like++`` is available since {cpp}23 and is useful when you want to forward a subobject of a forwarding reference.

return 0;
}
[source,cpp]
----
class Registry {
// ...
template <typename PairOfStrings>
void addNames(PairOfStrings&& arg) {
addName(std::forward_like<PairOfStrings>(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[]
Expand Down

0 comments on commit ee3c651

Please sign in to comment.