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 capturesthisby value. That is, it copies the value of the pointer that isthis.[&]captures*thisby reference. That is,thisin the lambda is a pointer to*thisoutside 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 thethispointer.[&, *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
[=]capturesthisas 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.
