85 lines
3.2 KiB
Plaintext
85 lines
3.2 KiB
Plaintext
== Why is this an issue?
|
|
|
|
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
|
|
|
|
[source,cpp]
|
|
----
|
|
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
|
|
|
|
[source,cpp]
|
|
----
|
|
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
|
|
|
|
* {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[]
|