
Update all links to C++ Core Guidelines to `e49158a`.
Refresh done using the following script and some manual edits:
db76e34e74/personal/fred-tingaud/rspec/refresh-cppcoreguidelines.py
When re-using this script, be mindful that:
- it does not cover `shared_content`
- it does not properly escape inline code in links (e.g., "[=]" or "`mutex`es")
- it does not change `C++` to `{cpp}` in link titles.
Co-authored-by: Marco Borgeaud <marco.borgeaud@sonarsource.com>
142 lines
6.1 KiB
Plaintext
142 lines
6.1 KiB
Plaintext
== Why is this an issue?
|
||
|
||
``++std::forward++`` and ``++std::move++`` have different purposes:
|
||
|
||
* ``++std::move++`` takes an object and casts it as an ``++rvalue++`` reference, which indicates that resources can be "stolen" from this object.
|
||
* ``++std::forward++`` has a single use-case: to cast a templated function parameter of type _forwarding reference_ (``++T&&++``) to the value category (``++lvalue++`` or ``++rvalue++``) the caller used to pass it. This allows ``++rvalue++`` arguments to be passed on as ``++rvalues++``, and ``++lvalues++`` to be passed on as ``++lvalues++``. This scheme is known as _perfect forwarding_. Note that the standard states that _"a forwarding reference is an rvalue reference to a cv-unqualified template parameter that does NOT represent a template parameter of a class template"_. Refer to the last noncompliant code example.
|
||
|
||
Since both rvalue references and forwarding references use the same notation (``++&&++``), an unwary developer might confuse them. If that happens, and a parameter is moved instead of forwarded, the original object can be emptied, probably crashing the software if the user tries to use the original object normally after the function call. An error in the other direction has less dire consequences and might even work as intended if the right template argument is used, but the code would be clumsy and not clearly express the intent.
|
||
|
||
|
||
This rule raises an issue when ``++std::forward++`` is used with a parameter not passed as a forwarding reference or when ``++std::move++`` is used on a parameter passed as a forwarding reference.
|
||
|
||
|
||
=== Noncompliant code example
|
||
|
||
[source,cpp,diff-id=1,diff-type=noncompliant]
|
||
----
|
||
#include <utility>
|
||
|
||
class S {};
|
||
|
||
template<typename T> void g(const T& t);
|
||
template<typename T> void g(T&& t);
|
||
|
||
template<typename T> void gt(T&& t) {
|
||
g(std::move(t)); // Noncompliant : std::move applied to a forwarding reference
|
||
}
|
||
|
||
void use_g() {
|
||
S s;
|
||
g(s);
|
||
g(std::forward<S>(s)); // Noncompliant : S isn't a forwarding reference.
|
||
}
|
||
|
||
template <typename T>
|
||
void foo(std::vector<T>&& t) {
|
||
std::forward<T>(t); // Noncompliant : std::vector<T>&& isn't a forwarding reference.
|
||
}
|
||
|
||
template<typename T>
|
||
struct C {
|
||
// In class template argument deduction, template parameter of a class template is never a forwarding reference.
|
||
C(T&& t) {
|
||
g(std::forward<T>(t)); // Noncompliant : T&& isn't a forwarding reference. It is an r-value reference.
|
||
}
|
||
};
|
||
----
|
||
|
||
|
||
=== Compliant solution
|
||
|
||
[source,cpp,diff-id=1,diff-type=compliant]
|
||
----
|
||
#include <utility>
|
||
|
||
class S {};
|
||
|
||
template<typename T> void g(const T& t);
|
||
template<typename T> void g(T&& t);
|
||
|
||
template<typename T> void gt(T&& t) {
|
||
g(std::forward(t));
|
||
}
|
||
|
||
void use_g() {
|
||
S s;
|
||
g(s);
|
||
g(std::move(s));
|
||
}
|
||
|
||
template <typename T>
|
||
void (std::vector<T>&& t){
|
||
std::move(t);
|
||
}
|
||
|
||
template<typename T>
|
||
struct C {
|
||
C(T&& t) {
|
||
g(std::move(t));
|
||
}
|
||
};
|
||
----
|
||
|
||
|
||
== Resources
|
||
|
||
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/move[std::move]
|
||
* {cpp} reference - https://en.cppreference.com/w/cpp/utility/forward[std::forward]
|
||
* {cpp} reference - https://en.cppreference.com/w/cpp/language/reference#Forwarding_references[Forwarding references]
|
||
* {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/e49158a/CppCoreGuidelines.md#f18-for-will-move-from-parameters-pass-by-x-and-stdmove-the-parameter[F.18: For "will-move-from" parameters, pass by `X&&` and `std::move` the parameter]
|
||
* {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[]
|
||
'''
|
||
== Comments And Links
|
||
(visible only on this page)
|
||
|
||
=== on 4 Jul 2019, 10:55:35 Geoffray Adde wrote:
|
||
``++Lambda functions++`` with ``++auto++`` parameters are also template in disguise. ``++auto&&++`` arguments should be treated as forwarding references.
|
||
|
||
=== on 26 Aug 2019, 22:08:13 Loïc Joly wrote:
|
||
Can you please review my changes?
|
||
|
||
=== on 9 Sep 2019, 17:35:47 Ann Campbell wrote:
|
||
\[~geoffray.adde] in SonarSource we've standardized on a (non-standard) spelling: Noncompliant. And in the compliant solution there's no need to explicitly mark anything compliant because by its nature everything in it is. I've corrected those things in this RSPEC, but for future reference...
|
||
|
||
|
||
Also, Geoffray and [~loic.joly], this RSPEC has no message.
|
||
|
||
|
||
For the references gentlemen, are you confident that these pages on en.cppreference.com will still be around 5 years from now?
|
||
|
||
|
||
And finally, it's not clear to me what Bad Thing will happen if you break this rule. Maybe that's because I'm not conversant in {cpp}. Maybe it's in there implicitly. But remember that we write rule descriptions not for language wizards but for the ones who still have something (a lot?) to learn. :-)
|
||
|
||
=== on 9 Sep 2019, 19:32:28 Loïc Joly wrote:
|
||
This website is already several years old, is well maintained and updated regularly. Is as become a de facto standard for {cpp}, so even is predictions are hard to make, especially the ones about the future :), we are as confident as can be...
|
||
|
||
|
||
For the message part, I don't think it is displayed anywhere in the rule description, and as such, is can only be used to communicated between the RSPECator and the guy who implements the rule, and is not necessary if they work closely together. Please correct me if I'm wrong. I'm reluctant to write the message, because quite often, when implementing the rule we discover special cases that require a message change.
|
||
|
||
|
||
Thank you for the bad things that can happen, I'll update the rspec.
|
||
|
||
=== on 3 Oct 2019, 23:41:48 Abbas Sabra wrote:
|
||
According to the {cpp}17 standard, there is a way to define class type deduction guide to make the constructor argument a forwarding reference. We should keep an eye on such example:
|
||
|
||
----
|
||
template <typename T>
|
||
struct A {
|
||
A(T&&); // Here T&& is forwarding reference because of the deduction guide defined belowe
|
||
};
|
||
template <typename T> A(T&&) -> A;
|
||
----
|
||
|
||
|
||
=== on 10 Oct 2019, 16:00:56 Geoffray Adde wrote:
|
||
\[~loic.joly], all changes are fine by me.
|
||
|
||
endif::env-github,rspecator-view[]
|