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.
[=]
, [this]
, [=, this]
and [&, this]
all captures this
by value. That is, it copies the value of the pointer that is this
.[&]
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 the this
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
};