Search code examples
c++lambdamove

C++ lambda ´this´ pointer invalidation after move operation


I have the following (simplified) code in my current project:

#include <iostream>
#include <string>
#include <functional>
#include <vector>


class Test{

public:

    Test() = default;
    Test(const Test& other) = delete;
    Test& operator=(const Test& other) = delete;
    Test(Test&& other) = default;
    Test& operator=(Test&& other) = default;



    void setFunction(){
       lambda = [this](){
           a = 2;
       };
    }  

    int callAndReturn(){
       lambda();
       return a;
    }

private:
    std::function<void()> lambda; 
    int a = 50;
};


int main()
{
  Test t;
  t.setFunction();
  std::vector<Test> elements;
  elements.push_back(std::move(t));
  std::cout << elements[0].callAndReturn() << std::endl;
}

When I run it, the value 50 is printed instead of the expected value 2. I suppose this happens because the lambda function captures the current this pointer. After the move operation the this pointer changes and the function writes to the wrong a.

Now my question is: Is there a way to change the lambda's captured reference to the new Test so that the value 2 is printed?


Solution

  • The solution is not to capture this at all. Instead, change your captured function type to accept it. And use a pointer to member (captured by value) for the indirect access to a.

    std::function<void(Test*)> lambda; 
    
    void setFunction(){
       auto a = &Test::a;
       lambda = [=](Test *t){
           (t->*a) = 2;
       };
    }  
    
    int callAndReturn(){
       lambda(this);
       return a;
    }
    

    Live Example


    As Galik noted, if you only need to access a single hard-coded member, then you don't even need that pointer to member. Thus the lambda can be capture-less:

    void setFunction(){
       lambda = [](Test *t){
           t->a = 2;
       };
    }