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?
This is because
foo
is allocated on the stack. The exact address offoo
depends on the call stack that led to the invocation ofdoSomething
. In other words, the address offoo
is likely to be different between function invocations, unless the call stack is exactly the same.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'sstatic
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, notstd::function
. So even iflambda
isstatic
, thestd::function
object is created in each call anyway (unless the whole thing is inlined).