Refer to this thread: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0806r2.html
It says:
In other words, one default capture ([&]) captures *this in the way that would be redundant when spelled out, but the other capture ([=]) captures it in the non-redundant way.
Which says that pre c 17, the [=] captures this
as value, and [&] will capture [*this], which is ambiguous. So I had a quick test, to see if [&] captures [*this] by default.
My test code trys to see if [&] defaultly captures *this, then copy ctor should be called, and any change to its value won't affect original object, as it is a copy.
#include<iostream>
using namespace std;
class M{
int mI;
public:
M() : mI(3) { cout << "ctor\n"; }
~M() { cout << "dtor\n"; }
M(const M& m) {
if (this != &m) {
cout << "copy ctor\n";
mI = m.mI;
}
}
M& operator=(const M& m) {
if (this != &m) {
cout << "operator =\n";
mI = m.mI;
}
return *this;
}
void CaptureByValue() {
auto f1 = [=] () { // capture this
cout << mI << '\n';
(this->mI);
};
f1();
cout << mI << '\n';
}
void CaptureByReference() {
auto f1 = [&] () { // capture *this
cout << mI << '\n';
(this->mI);
};
f1();
cout << mI << '\n';
}
};
int main() {
{
M obj1;
obj1.CaptureByValue();
}
cout << "------------\n";
{
M obj2;
obj2.CaptureByReference();
}
return 0;
}
Compile and run it with:
clang LambdaCapture.cpp -std=c 11 && ./a.out
clang LambdaCapture.cpp -std=c 14 && ./a.out
clang LambdaCapture.cpp -std=c 17 && ./a.out
All cases print:
ctor
3
4
dtor
------------
ctor
3
4
dtor
My questions:
(1) The result is out of my expectation: no copy ctor is called by CaptureByReference
and the value being changed affected original this
object. The test code didn't promoted the lambda syntax change in cpp17.
(2) I can't even change my capture into [=, *this] or [&, *this] as compiler will say:
LambdaCapture.cpp:25:13: error: read-only variable is not assignable
(this->mI);
^ ~~~~~~~~~~
Very strange, how came out a read-only
variable here, as to this
pointer.
Appreciate your explanations.
CodePudding user response:
[=]
,[this]
,[=, this]
and[&, this]
all capturesthis
by value. That is, it copies the value of the pointer that isthis
.[&]
captures*this
by reference. That is,this
in the lambda is a pointer to*this
outside the lambda.
The effect of the above versions with regards to this
in the lambda will therefore be the same.
[=, *this]
copies all elements captured and also makes a copy of*this
- not thethis
pointer.[&, *this]
makes a reference to all elements captured but makes a copy of*this
.
LambdaCapture.cpp:25:13: error: read-only variable is not assignable
(this->mI);
^ ~~~~~~~~~~
That's because lambdas are const
by default. You need to make them mutable
in order to be able to change them.
auto f1 = [&, *this]() mutable { // made mutable
cout << mI << '\n';
(this->mI); // now ok
};
CodePudding user response:
Thinking in terms of "capturing this
" can be confusing. Think in terms of "capturing *this
" (the current class instance).
[&]
and [=]
have exactly the same meaning with respect to *this
: both capture *this
by reference.
This makes sense for [&]
. But this is weird for [=]
, since it captures by value otherwise. This is why C 20 deprecates the capture of this
by [=]
, forcing you to manually spell either [this]
(by reference) or [*this]
(by value).
pre c 17, the
[=]
capturesthis
as value, and[&]
will capture[*this]
, which is ambiguous. So I had a quick test, to see if[&]
captures[*this]
by default.
Nope, you misunderstood the quote. Both [=]
and [&]
capture *this
by reference (aka this
by value), there is no ambiguity.
As I see it, the quote just points out the inconsistency of [=]
capturing *this
by reference.
I can't even change my capture into
[=, *this]
or[&, *this]
as compiler will say:error: read-only variable is not assignable
If you want to modify any by-value capture, the lambda needs to be mutable
.