Muttal Kadavul
Muttal Kadavul

Reputation: 49

Capturing only a specific member of a class inside lambda

I understand that we have to capture the *this pointer in the lambda to access any of the member variables inside the lambda. Now, why can't I capture only a single member of an object inside the lambda?

 #include <iostream>

class Foo
{
public:
    Foo () : a(5) {}
    void func ()
    {
        [this->a] () { std::cout << a; } ();
    }

private:
        int a;
};

int main()
{
    Foo f;
    f.func();
}

This code above throws me a syntax error,

<source>: In member function 'void Foo::func()':
<source>:9:14: error: expected ',' before '->' token
    9 |         [this->a] () { std::cout << a; } ();
      |              ^~
<source>:9:14: error: expected identifier before '->' token

I tried this in godbolt. org with gcc 11.1

[Edit] I understand that there are ways to capture a single member of an object but I want to understand why this->member is invalid in the capture list.

Upvotes: 2

Views: 1249

Answers (4)

armagedescu
armagedescu

Reputation: 2160

You can simply expose pointer this

class Foo
{
    int a;
public:
    Foo () : a(5) {}
    void func ()
    {
        [this] () { std::cout << a; } ();
    }

};

Upvotes: 0

rog
rog

Reputation: 113

void func ()
{
    [a = this->a] () { std::cout << a; } ();
}

You need to specify the name of the variable to access it in the lambda.

EDIT:

Lambda under the hood:

When a lambda definition is executed, for each variable that the lambda captures, a clone of that variable is made (with an identical name) inside the lambda. These cloned variables are initialized from the outer scope variables of the same name at this point.

So [this->a] doesn't make sense. Compiler need to allocate space for captured variable, and to make that memory accessible it needs a name.

Upvotes: 3

Yksisarvinen
Yksisarvinen

Reputation: 22176

but my question is why is this not valid

First thing to understand is how are lambdas expressed behind the scenes. Compiler replaces it with an anonymous class with overloaded operator(). For example, a following lambda:

[a](int b){return a + b;}

Could be converted by the compiler to following class:

class __lambda_some_random_unique_identifier {
public:
    int operator() (int b) const {
        return a + b;
    }
private:
    int a;
};

Now, if you would allow expression this->a to be on capture list, how should it be translated to a class member? Arguably, it could be called a, but if you allow expression this->a, then why not this->a.size() (assuming a is std::vector)? How would it be named then?


Since the problem can be easily solved with named captures or with capturing this as a whole, there's no good reason to add handling complex expressions to that.

Upvotes: 3

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

Its just a matter of what syntax is valid and what is not. Your issue isn't specific to this. You also cannot capture member of another object like this:

struct foo { int x; };

int main() {
    foo obj;
    foo* f = &obj;
    auto l = [f->x](){};
}

While this is ok:

struct foo { int x; };

int main() {
    foo obj;
    foo* f = &obj;
    auto l = [x = f->x](){};
}

Upvotes: 1

Related Questions