While using some local lambda objects in a C++11 function I was tempted to declare them as const static auto lambda = ...
just to let the compiler know that there is just one std::function
object needed (and possibly optimize the call and/or inline it) but I realized that capturing local values by reference in this circumstance leads to weird behavior.
Consider the following code:
void process(const Data& data, const std::function<void(DataElement&>& lambda) {
...
}
void SomeClass::doSomething()
{
int foo = 0;
const static auto lambda = [&foo] () { .... ++foo; .... }
process(data, lambda);
}
This doesn't work with multiple invocations of doSomething()
but the mechanics is not clear.
- Is
foo
bound at the first invocation and then kept bound to a stack address which becomes invalid on successive invocations?
- Am I forced so drop
static
in this circumstance?
Where is this behavior specified in the standard? Considering it's a static
variable where is it constructed? Lazily on first invocation of doSomething()
(so that the first invocation works) or at program start?
A static function-scope variable is initialised "lazily," when control flow first reaches its declaration. This means that the capture by reference does indeed bind to the foo
currently on stack, and when the call terminates, that binding becomes dangling.
Don't try to help the compiler too much; making lambda
static
looks like a micro-optimisation, with very bad side effects. There's next to no overhead involved in actually creating a closure object, and the compiler can easily inline it regardless of whether it's static
or not.
Not to mention the fact that you're not saving on creating the std::function
object even with your approach. The type of a lambda expression is an unnamed closure object, not std::function
. So even if lambda
is static
, the std::function
object is created in each call anyway (unless the whole thing is inlined).
This doesn't work with multiple invocations of doSomething()
but the mechanics is not clear.
This is because foo
is allocated on the stack. The exact address of foo
depends on the call stack that led to the invocation of doSomething
. In other words, the address of foo
is likely to be different between function invocations, unless the call stack is exactly the same.