I have a situation where I have a lambda as a member variable that is created by a certain function call. The problem is that it captures this as a part of its operation. Later on, I want to be able to copy the whole object...
However, at the time of the copy I don't know how the lambda was created (it could have been defined in several spots through different code paths). Hence, I'm somewhat at a loss as to what to put in the copy constructor. Ideally, I would want to "rebind" the lambda's captures to the new "this" that was created.
Is this at all possible?
Here's some sample code:
#include <iostream>
#include <string>
#include <functional>
class Foo
{
public:
Foo () = default;
~Foo () = default;
void set (const std::string & v)
{
value = v;
}
void set ()
{
lambda = [&]()
{
return this->value;
};
}
std::string get ()
{
return lambda();
}
std::string value;
std::function <std::string (void)> lambda;
};
int main ()
{
Foo foo;
foo.set ();
foo.set ("first");
std::cerr << foo.get () << std::endl; // prints "first"
foo.set ("captures change");
std::cerr << foo.get () << std::endl; // prints "captures change"
Foo foo2 (foo);
foo2.set ("second");
std::cerr << foo.get () << std::endl; // prints "captures change" (as desired)
std::cerr << foo2.get () << std::endl; // prints "captures change" (I would want "second" here)
return 0;
}
Thanks in advance.
The problem you're seeing is that the this
pointer is being captured into the lambda, but you're now executing the copy of the function from another object. It's working in your example because both objects exist, but it's a dangling pointer waiting to happen.
The cleanest way to do this is to modify your std::function
and your lambdas to take an argument of a pointer to the class, and use that passed in pointer instead of capturing this. Depending on the contents of your lambda, you can choose to capture the values.
class Foo
{
public:
Foo () = default;
~Foo () = default;
void set (const std::string & v)
{
value = v;
}
void set ()
{
lambda = [](Foo* self)
{
return self->value;
};
}
std::string get ()
{
return lambda(this);
}
std::string value;
std::function <std::string (Foo*)> lambda;
};
Example at IDEOne
I don't think you can modify the closure. If you need the function to operate on another object, you need to pass the pointer to the object as an argument to the function:
class Foo
{
public:
Foo () = default;
~Foo () = default;
void set (const std::string & v)
{
value = v;
}
void set ()
{
lambda = [](Foo* t)
{
return t->value;
};
}
std::string get ()
{
return lambda(this);
}
std::string value;
std::function <std::string (Foo*)> lambda;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With