rspec/rules/S5019/cfamily/rule.adoc

85 lines
3.2 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2021-04-28 16:49:39 +02:00
A lambda can only capture local variables. When a lambda is defined within a member function, you may believe that you are capturing a member variable of the current class, but in fact, what you are capturing is ``++this++``. This may be very surprising, and lead to bugs if the lambda is then used after the current object has been destroyed.
Therefore, it's better to be explicit about exactly what is captured as soon as ``++this++`` is captured.
If the lambda is used immediately (for instance, called or passed as an argument to ``++std::sort++``), there is no such risk and no issue is raised.
In {cpp}20, capturing ``++this++`` via [=] has been deprecated. An issue is raised in that case, even if the lambda is used immediately.
Note: This rule does not apply if the capture list of the lambda contains ``++*this++`` (possible since {cpp}17). In that situation, what is captured is not the pointer ``++this++``, but a local copy of the object pointed-to by ``++this++`` and any reference to ``++this++`` (explicit or implicit) in the lambda body then refers to this local copy (see S6016).
=== Noncompliant code example
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,cpp]
2021-04-28 16:49:39 +02:00
----
void useLambda(std::function<int,int> lambda);
class A {
int i;
void f(int j) {
auto l = [=](int k) { return i+j+k;}; // Noncompliant, someone reading the code might believe that i is captured by copy
useLambda(l);
}
};
----
=== Compliant solution
2021-04-28 16:49:39 +02:00
2022-02-04 17:28:24 +01:00
[source,cpp]
2021-04-28 16:49:39 +02:00
----
void useLambda(std::function<int,int> lambda);
class A {
int i;
void f(int j) {
auto l = [this, j](int k) { return i+j+k;}; // It is now clearer that i is not directly captured
useLambda(l);
// auto l = [i, j](int k) { return i+j+k;}; // Would not compile
auto l2 = [=, *this](int k) { return i+j+k;}; // Compliant, i refers to the member i of the captured copy
useLambda(l2);
auto l3 = [=](int k) { return i+j+k;}; // Compliant because l3 is only used immediately
int ijk = l3(i,j,k);
}
};
----
== Resources
2021-04-28 16:49:39 +02:00
* {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/e49158a/CppCoreGuidelines.md#f54-when-writing-a-lambda-that-captures-this-or-any-class-data-member-dont-use\--default-capture[F.54: When writing a lambda that captures `this` or any class data member, don't use ``++[=]++`` default capture]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Explicitly capture all local variables required in this lambda.
'''
== Comments And Links
(visible only on this page)
=== on 8 Nov 2018, 19:29:55 Ann Campbell wrote:
\[~loic.joly] please double-check "this" RSpec against RSPEC-3608. Without closely reading both I think there may be overlap if not duplication.
=== on 8 Nov 2018, 19:38:41 Loïc Joly wrote:
\[~ann.campbell.2] There is overlap, but I believe that RSPEC-3608 is much too strict (even if something similar could appear in the next Misra standard), and I would clearly not enable it in one of my codebases, while I would enable this one.
I'm also thinking one another related rule, which would be something like "Lambdas that outlive their definition scope should not implicitely capture by reference". If I can have this one, I will probably remove RSPEC-3608 from SonarWay.
endif::env-github,rspecator-view[]