Reputation: 14409
I'm struggling to understand the scope and ownership of lambda captures, why does the following code work?:
#include <iostream>
struct Foo {
void setCallback(std::function<void()> f) { callback = std::move(f); }
void trigger() { callback(); }
private:
std::function<void()> callback;
};
struct Bar {
int i;
Bar(int i) : i{i} { std::cout << "bar constructed" << std::endl; }
~Bar() { std::cout << "bar destroyed" << std::endl; }
};
int main() {
Foo foo{};
{ // scope
Bar bar{1};
foo.setCallback([&bar]() { std::cout << bar.i << std::endl; });
bar.i = 2;
} //end of scope, bar gets deleted
foo.trigger();
return 0;
}
Output:
bar constructed
bar destroyed
2
Is the compiler inlining bar.i
in the lambda function statically? I'm capturing by reference, what is going on?
Upvotes: 1
Views: 255
Reputation: 3323
Hello Your code does not work because bar is descoped before the lambda that captured it is executed. The lambda uses a dangling reference. Here is an updated sample that will convince you, that if it works in your example it is simply because nothing else has been allocated at the address where bar was initially stored.
#include <iostream>
#include <functional>
struct Foo {
void setCallback(std::function<void()> f) { callback = std::move(f); }
void trigger() { callback(); }
private:
std::function<void()> callback;
};
struct Bar {
int i;
Bar(int i) : i{i} { std::cout << "bar constructed" << std::endl; }
~Bar() { std::cout << "bar destroyed" << std::endl; }
};
int main() {
Foo foo{};
{ // scope
Bar bar{1};
foo.setCallback([&bar]() { std::cout << bar.i << std::endl; });
bar.i = 2;
} //end of scope, bar gets deleted
volatile int a = 5; // allocate something after bar is destroyed
foo.trigger();
return 0;
}
Now you will get:
bar constructed
bar destroyed
5
5 is the next value allocated on the stack in place of bar.
Upvotes: 2