Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing lambda capture initialized variable outside the lambda in C++

In C++14/17, how do you access a lambda capture initialized variable outside the scope of the lambda?

Source:

#include <iostream>

using namespace std;

int main(){
    auto test = [value1 =0]() mutable {value1+=1; return value1;};
    cout << test() << endl;
    cout << test() << endl;
    //cout << value1 << endl;//error: ‘value1’ was not declared in this scope
}

Output:

1

2

Is the value1 variable accessible outside the scope of the test() lambda? What is the lifetime of a lambda capture initialized variable?

Attempting to access value1 outside the lambda gives the following error: ‘value1’ was not declared in this scope.

Compiled with gcc version 7.3.0 (Ubuntu 7.3.0-21ubuntu1~14.04).

like image 983
Eugene Avatar asked Oct 21 '18 02:10

Eugene


People also ask

How do you capture a variable in lambda function?

Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.

Can lambda function access global variables?

If you think about it a lambda is just a short-cut to defining a struct with a function operator. Local variables are not in scope for struct member functions but global variables are. Global variables can't be captured.

What is capture clause in lambda function in C++?

A capture clause of lambda definition is used to specify which variables are captured and whether they are captured by reference or by value. An empty capture closure [ ], indicates that no variables are used by lambda which means it can only access variables that are local to it.

What does it mean to lambda capture this?

A lambda is a syntax for creating a class. Capturing a variable means that variable is passed to the constructor for that class. A lambda can specify whether it's passed by reference or by value.


2 Answers

A lambda is just a compact definition for an inline-defined struct and an operator() overload on that struct (and for creating an object of that struct's type). Lambda "captures" are just member variables of this struct, initialized by the type's constructor. This is one reason why C++ lambdas have to have syntax for capturing by value vs. by reference.

But the members variables of the struct are private. And since the compiler-generated struct is largely implementation-defined, it isn't required by the standard to expose those members with those names. The compiler-generated struct could use some other name if it wanted; the compiler would just have to remap the in-lambda usage of those names to reference the members' names.

So no, lambda captures of any kind cannot be accessed by the world outside of that lambda. If you capture a reference to an object, then its possible for the outside world to access the same object. But you would not be accessing the same reference to that object.

like image 50
Nicol Bolas Avatar answered Oct 24 '22 20:10

Nicol Bolas


Just for completeness, depending on the compiler it is technically possible to access the internal members of the lambda, and modify them. Although this is basically taking advantage of an implementation detail, and should never be done. But it does provide some insight into the lambda implementation.

Here it is in GCC 6.3

#include <iostream>

using namespace std;

template<typename Lambda>
struct lambda_member : Lambda
{

    Lambda& f_;
    lambda_member(Lambda& f) : Lambda(f),
        f_(f)
    {}

    auto& get_value1()
    {
        return f_.__value1;
    }

};


int main(){
    auto test = [value1 =0]() mutable {value1+=1; return value1;};

    lambda_member<decltype(test)> lm{test};

    std::cout << test() << std::endl;
    std::cout << lm.get_value1() << std::endl;
    lm.get_value1() = 22;
    std::cout << test() << std::endl;

}

Demo

like image 27
rmawatson Avatar answered Oct 24 '22 18:10

rmawatson