I have a nested lambda in C++, which is to say, an inner lambda contained in a middle lambda, which is also contained in an outer lambda.
I created a shared_ptr in the outer lambda, which I passed by value to the middle lambda, inside which I created the inner lambda, after declaration of which the captured shared_ptr seems to be released, as can be confirmed with use_count()
turning into zero. Why?
The full code:
#include <cstdio>
#include <functional>
#include <memory>
struct container;
using func_type = std::function<void(container&)>;
struct container {
explicit container(int id, func_type next) : id{id}, next{next} {
printf("contianer (%d)\n", id);
}
func_type next;
int id;
~container() {
printf("~contianer (%d)\n", id);
}
};
struct value_type {
~value_type() {
printf("~value_type\n");
}
};
int main() {
container c{0, [](container& c1) {
std::shared_ptr<value_type> value = std::make_shared<value_type>();
c1 = container{1, [value](container& c2) mutable {
printf("value.use_count(): %d\n", value.use_count());
c2 = container{2, [](container& c3) mutable {
printf("finished\n");
return;
}};
printf("value.use_count(): %d\n", value.use_count());
return;
}};
return;
}};
c.next(c);
c.next(c);
return 0;
}
Output: (godbolt: https://godbolt.org/z/9PbboEPfK)
Program returned: 0
Program stdout
contianer (0)
contianer (1)
~contianer (1)
value.use_count(): 1
contianer (2)
~value_type
~contianer (2)
value.use_count(): 0
~contianer (2)
When you call c.next(c)
the first time, you are running the function c.next
which will cause c.next
to be replaced by a new lambda, the one that owns a shared pointer. After the first c.next(...)
call this shared_ptr owning lambda will be the new c.next
.
When you then call c.next(c)
again you are replacing that lambda with one that has no ownership of the shared_ptr so the shared_ptr gets destructed as it should when the replacement assignment happens as nothing else has a reference to it except a closure that is going out of scope.
If this was real code -- which I mean if it's real code, do not do whatever you are doing this way -- you could get the behavior you seem to want by having the inner lambda, the one that just prints "finished", also capture value
.