I have some code where a non-generic lamba expression has a local static variable: a mutex for a critical section. Something that can be simplified to:
int i = 0;
auto lambda = [&i](int &v)
{
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
/* critical section with v and i */};
}
Now, the critical section implemented in this lambda expression could be reused verbatim for other types than int &
and I would have liked a change as simple as replacing int
with auto
in the declarator, like this:
auto lambda = [&i](auto &v)
Unfortunately, if I do this, lambda(int&)
and lambda(float&)
won't share the same local static variable anymore, which will defeat the lock in the critical section.
What would be the simplest change in the code that would meet all these requirements:
A solution that would work would be to replace the lambda expression with a class with a template method, something like this:
class Lambda
{
public:
Lambda(int &i) : i_(i) {}
template<class V>
void operator()(V &v)
{
std::lock_guard<std::mutex> lock(mutex_);
/* critical section with v and i_ */
};
private:
static std::mutex mutex_;
int &i_;
};
std::mutex Lambda::mutex_;
int i = 0;
Lambda lambda(i);
This would work (with caveats like the private reference i_
), but it looks very cumbersome compared to the initial lambda expression. Would there be anything more straightforward?
A lambda or anonymous method may have a static modifier. The static modifier indicates that the lambda or anonymous method is a static anonymous function. A static anonymous function cannot capture state from the enclosing scope.
The [=] you're referring to is part of the capture list for the lambda expression. This tells C++ that the code inside the lambda expression is initialized so that the lambda gets a copy of all the local variables it uses when it's created.
A lambda function is a stateless instantatiation of a function interface because the lambda expression syntax does not allow for the declaration of instance variables (fields). Therefore, there is no statically-defined data and no other methods to be accessed, negating the need for a self-reference.
A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.
You can init-capture a shared_ptr, which will be similar to having it defined as a member variable of the closure type, and so all copies of the closure object will share the same mutex:
auto lambda = [&i, m = std::make_shared<std::mutex>()](auto &v)
{
std::lock_guard<std::mutex> lock(*m);
/* critical section with v and i */
};
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