Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Can't Member Variables Be Used Unqualified In A Lambda If Non-Captured Local Variables Exist?

Simple example:

class Foo {
    int x;

    void bar(int x) {
        [this]() -> void {
            x = 6;
        }();
    }
};

This doesn't compile on GCC, Clang, MVC, or ICC (see it live). If I change void bar(int x) to void bar(int y), or if I change x = 6; to this->x = 6; then it works fine.

This doesn't make sense to me. The local variable x from the call to bar is deliberately not being captured in the lambda. The only x that makes sense is Foo's member variable.

Question: Is this the expected behavior, and iff so, please explain why?

like image 212
imallett Avatar asked May 20 '17 18:05

imallett


People also ask

How do you capture a member variable in lambda?

To capture the member variables inside lambda function, capture the “this” pointer by value i.e. std::for_each(vec. begin(), vec. end(), [this](int element){ //.... }

Can you pass local variables to lambda expressions?

A lambda expression can't define any new scope as an anonymous inner class does, so we can't declare a local variable with the same which is already declared in the enclosing scope of a lambda expression. Inside lambda expression, we can't assign any value to some local variable declared outside the lambda expression.

Can we use instance variable in lambda expression?

Yes, but instance variables can be referenced and assigned in a lambda, which is surprising to me. Only local variables have the final limitation.


1 Answers

From cppreference:

For the purpose of name lookup, determining the type and value of the this pointer and for accessing non-static class members, the body of the closure type's function call operator is considered in the context of the lambda-expression.

In the body of the method bar(int x), the token 'x' refers to the argument of the method, NOT to the member of the class. The member is hidden. [NOTE this is certainly poor coding practice, but not illegal]

If you were to say x = 6 where you currently have the lambda definition, you would expect the local variable x (i.e. the passed-by-value-argument to the method) to be changed, not the member x, right?

Thus the only question is can a local variable be implicitly captured by a lambda? I'd say the compilers are pretty clear in explaining that it cannot.

cppreference also makes this clear in this statement:

A variable can be used without being captured if it does not have automatic storage duration (i.e. it is not a local variable or it is static or thread local) or if it is not odr-used in the body of the lambda.

Note: odr-used means you have to know the address of a variable, not just its value. Assigning a value to a variable counts as odr-using it.

Consider this code:

class Foo {
    int george;

    void bar(int washington) {
        int martha = washington;
        washington = 7;
        int jefferson = washington;
        int adams = martha;
        george = 6;
        [this, jefferson]() -> void {
            this->george = 15; // legal because `this` is captured
            jefferson = adams; // legal because jefferson is explicitly
                               // captured, and because adams is not
                               // odr-used, so adams can be captured
                               // implicitly.
            martha = 9; // not legal because it is an odr-use 
                        // of a local variable so martha is not 
                        // implicitly captured.
        }();
    }
like image 154
Dale Wilson Avatar answered Oct 20 '22 21:10

Dale Wilson