简体   繁体   中英

Why can't I capture this by-reference ('&this') in lambda?

I understand the correct way to capture this (to modify object properties) in a lambda is as follows:

auto f = [this] () { /* ... */ };

But I'm curious as to the following peculiarity I've seen:

class C {
    public:
        void foo() {
            // auto f = [] () { // this not captured
            auto f = [&] () { // why does this work?
            // auto f = [&this] () { // Expected ',' before 'this'
            // auto f = [this] () { // works as expected
                x = 5;
            };
            f();
        }

    private:
        int x;
};

The oddity that I am confused by (and would like answered) is why the following works:

auto f = [&] () { /* ... */ }; // capture everything by reference

And why I cannot explicitly capture this by reference:

auto f = [&this] () { /* ... */ }; // a compiler error as seen above.

The reason [&this] doesn't work is because it is a syntax error. Each comma-seperated parameter in the lambda-introducer is a capture :

capture:
    identifier
    & identifier
    this

You can see that &this isn't allowed syntactically. The reason it isn't allowed is because you would never want to capture this by reference, as it is a small const pointer. You would only ever want to pass it by value - so the language just doesn't support capturing this by reference.

To capture this explicitly you can use [this] as the lambda-introducer .

The first capture can be a capture-default which is:

capture-default:
    &
    =

This means capture automatically whatever I use, by reference ( & ) or by value ( = ) respectively - however the treatment of this is special - in both cases it is captured by value for the reasons given previously (even with a default capture of & , which usually means capture by reference).

5.1.2.7/8:

For purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming id- expressions referring to non-static class members into class member access expressions using (*this) (9.3.1), the compound-statement [OF THE LAMBDA] is considered in the context of the lambda-expression.

So the lambda acts as if it is part of the enclosing member function when using member names (like in your example the use of the name x ), so it will generate "implicit usages" of this just like a member function does.

If a lambda-capture includes a capture-default that is & , the identifiers in the lambda-capture shall not be preceded by & . If a lambda-capture includes a capture-default that is = , the lambda-capture shall not contain this and each identifier it contains shall be preceded by & . An identifier or this shall not appear more than once in a lambda-capture.

So you can use [this] , [&] , [=] or [&,this] as a lambda-introducer to capture the this pointer by value.

However [&this] and [=, this] are ill-formed. In the last case gcc forgivingly warns for [=,this] that explicit by-copy capture of 'this' redundant with by-copy capture default rather than errors.

Because standard doesn't have &this in Captures lists:

N4713 8.4.5.2 Captures:

lambda-capture:
    capture-default
    capture-list
    capture-default, capture-list

capture-default:
    &
    =
capture-list:
    capture...opt
    capture-list, capture...opt
capture:
    simple-capture
    init-capture
simple-capture:
    identifier
    &identifier
    this
    * this
init-capture:
    identifier initializer
    &identifier initializer
  1. For the purposes of lambda capture, an expression potentially references local entities as follows:

    7.3 A this expression potentially references *this.

So, standard guarantees this and *this is valid, and &this is invalid. Also, capturing this means capturing *this (which is a lvalue, the object itself) by reference , rather than capturing this pointer by value !

I am not sure I understand the question properly, so my answer my not be appropriate in the context of the question.

If you want to modify the values in the class in a lambda, then you could:

class C {
public:
    void foo() {
        C* this_ptr_2 = this;
        auto f = [&this_ptr_2]{
            this_ptr_2->x = 69; // Modify a class member.
        };
        f();
    }

private:
    int x;
};

Again, I don't know if this answers your question or not. But if you meant that you wanted to modify class members in a lambda. I guess the above should suffice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM