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).
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.
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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With