Home > front end >  C 11/14/17 Lambda reference capture [&] doesn't copy [*this]
C 11/14/17 Lambda reference capture [&] doesn't copy [*this]

Time:08-02

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 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
};

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 [=] captures this 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.

  • Related