mallwright
mallwright

Reputation: 1975

Destructor running twice on class instance declared inside lambda capture specifier

When running the following code, it seems that the destructor is running twice. I have a theory that this might have to do with an automatic move constructor being added, but I'm not sure how to test this.

#include <iostream>
#include <functional>

struct Structure {
   Structure(int n) :
      Value(n) {
      std::cout << "constructor: " << Value << std::endl;
   }
   ~Structure() {
      std::cout << "destructor: " << Value << std::endl;
   }
   int Value;
};

int main() {
   int Init = 4;
   std::function<void()> Function = [Instance = Structure(Init)] () {
      std::cout << "Value is: " << Instance.Value << std::endl;
   };
   Function();
   Function();
   return 0;
}

Output:

constructor: 4
destructor: 4
Value is: 4
Value is: 4
destructor: 4

Is this output correct?

Upvotes: 1

Views: 269

Answers (2)

mallwright
mallwright

Reputation: 1975

Ok, I defined the move and copy constructors manually and also saw errors when I instructed the compiler to delete them in a variant of the code below. Everything seems normal.

Revised code:

#include <iostream>
#include <functional>

struct Structure {
   Structure(int n) :
      Value(n) {
      std::cout << "constructor: " << Value << std::endl;
   }

   Structure(const Structure& other) :
      Value(other.Value) {
      std::cout << "copy constructor: " << Value << std::endl;
   }

   Structure(Structure&& other) :
      Value(other.Value) {
      other.Value = -1;
      std::cout << "move constructor: " << Value << std::endl;
   }

   ~Structure() {
      std::cout << "destructor: " << Value << std::endl;
   }
   int Value;
};

int main() {
   int Init = 4;
   std::function<void()> Function = [Instance = Structure(Init)] () {
      std::cout << "Value is: " << Instance.Value << std::endl;
   };
   Function();
   Function();
   return 0;
}

Output:

constructor: 4
move constructor: 4
destructor: -1
Value is: 4
Value is: 4
destructor: 4

Upvotes: 1

Quentin
Quentin

Reputation: 63144

std::function works by copying the callable object you provide. There is no copy-elision here, since your lambda is not an std::function but of an anonymous, unrelated type.

Hence, the two destructors you see are:

  • The Instance member of the original, temporary, lambda;

  • The Instance member of the copy of the lambda that was stored into the std::function and lived until the end of main.

Upvotes: 2

Related Questions