Search code examples
c++lambdashared-ptr

Captured shared_ptr released while executing the lambda body


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)

Solution

  • 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.