在使用基于回调的事件系统时,在构造函数中构造 lambda 时,我遇到了一些严重的错误。 这是简单的场景:
class Object {
public:
int variable = 10;
std::function<void()> callback;
Object() {
callback = [this]() {
variable += 10;
};
}
};
class ObjectReceiver {
public:
Object object;
explicit ObjectReceiver(Object &&hi) : object(std::move(hi)) {}
};
int main() {
ObjectReceiver receiver{Object()};
receiver.object.callback();
std::cout << receiver.object.variable << std::endl;
return 0;
}
这里将打印“10”,因为对象被移动,并且 lambda 所持有的指针失效。
这意味着在
Object
之外的代码中,我应该始终关心它是否在其构造函数中构造 lambda。
遇到此类问题如何处理?我应该为自己制定一个通用约定,不要从构造函数构造 lambda 表达式吗?
问题在于,调用
receiver.object.callback();
使用参数 this
进行操作,该参数指向由 Object
中的构造函数 Object()
创建的 ObjectReceiver receiver{Object()};
的临时实例。即使您按值传递所有内容并完全避免移动语义,您仍然会遇到相同的问题。根本问题是,当应用移动语义将 this
的临时实例复制到存储在 Object
中的实例时,receiver.object
是按值传递的,并且不会更新。
要使其工作,您需要使 lambda 相对于它所应用的
Object
实例,如下所示:
class Object {
public:
int variable = 10;
std::function<void(Object& o)> raw_callback;
void callback() { raw_callback (*this); }
Object() {
raw_callback = [](Object& o) {
o.variable += 10;
};
}
};