周围还有类似的问题:
operator()
template <class Function>
void storeCallback(const Function& f)
以下方式调用安全吗?
void foo(Storage& o) {
o.storeCallback([](){std::cout << "hi!";});
}
void bar(Storage& o){
o.callCallback();
}
我看到的风险是,一旦
foo
返回,lambda 就会超出范围。那么当将来某个时候调用存储的回调时,引用将无效?
在我感兴趣的特定情况下,引发了这个问题,lambda 存储在存储类中的具体情况是,它被传递到另一个方法,该方法将std::function
按值作为参数。这种从引用到
std::function
的转换是否以某种方式使其安全?或者无论如何存储它都已经安全了?std::function
文档状态
std::function
的实例可以存储、使它安全,因为它无论如何都会复制 lambda,即使它是通过引用传递的。复制
和调用任何CopyConstructible Callable目标——函数(通过指向其的指针)、lambda表达式、绑定表达式或其他函数对象,以及指向成员函数的指针以及指向数据成员的指针。 这对我来说听起来像std::function
在您编写的代码中,您肯定使用了某种类型擦除(如
std::function
,那么你一定会拥有 lambda 的副本。这是因为复制到
std::function
会在
foo
超出范围之前发生。如果您的存储保存对 lambda 的引用而不是复制它,就像
std::function_ref
那样,这将导致未定义的行为。但请注意,由于您的 lambda 不捕获任何内容,因此即使行为实际上未定义,这也很可能会为您提供正确的结果。
另外,不要混淆通过引用捕获和通过引用传递。后者通常是安全的,前者通常不是。以这段代码片段为例:
class MyClass
{
void foo(Storage& o) {
o.storeCallback([&]() { /* some calculation using data members */ });
}
};
int main() {
{
MyClass obj{};
obj.foo(my_storage);
}
// obj goes out of scope
my_storage.callCallback(); // oops! UNDEFINED BEHAVIOR
return 0;
}
这里,因为通过引用捕获,
this
也被捕获了!当 obj
超出范围时,调用存储的 lambda 是未定义的行为。这就是为什么您必须非常小心地通过引用进行捕获。