通过 const std::function 引用传递临时 lambda 应该会失败,但似乎可以工作
passing a temporary lambda by const std::function reference should fail but seems to work
这是我所做的简化版本:
#include <iostream>
#include <functional>
class thing
{
public:
void register_fn(const std::function<void()> &fn)
{
m_fn = fn;
}
void run()
{
m_fn();
}
std::function<void()> m_fn;
};
int main() {
// Create a thing object
thing t;
// In a limited scope
{
// Local lambda
auto afn = []{std::cout << "hi\n";};
// Store the lamda by reference
t.register_fn(afn);
}
// Run the stored lambda (which should be destroyed - therefore dangling reference??)
t.run();
// Take a copy
thing t2 = t;
t2.run();
return 0;
}
在此处查看 运行:https://godbolt.org/z/6qW3ro
所以,我有一个 class 存储通过引用传递的临时 lambda。 lamda afn
的范围是有限的,因此一旦它被传递给 register 函数,它就会超出范围。然后在这个范围之外,我调用 run
函数,它应该是 运行 对 lambda 的(悬空?)引用。
这一直有效,但最近回顾我的代码我有疑问。由于 lambda 是临时的(在这里通过限制我的 lambda 对象 afn
的范围来完成) - 这不应该起作用......我无法完全理解它为什么起作用 - 除非运气好,这是未定义的行为?
或者...我在这里误解了什么? - 因为这可能是最有可能的解释!
这主要是因为您复制了函数对象。在
void register_fn(const std::function<void()> &fn)
{
m_fn = fn;
}
你将 fn
赋值给 m_fn
来制作副本,即使 fn
是对本地 lambda 的引用,制作副本也意味着 m_fn
不会引用 fn
,但会得到存储在其中的函数 fn
的副本。这意味着没有悬空引用并且您的代码具有明确定义的行为。
如果 lambda 通过引用捕获本地对象,情况会有所不同,因为在您离开声明 lambda 的范围后,该捕获将失效。
这是我所做的简化版本:
#include <iostream>
#include <functional>
class thing
{
public:
void register_fn(const std::function<void()> &fn)
{
m_fn = fn;
}
void run()
{
m_fn();
}
std::function<void()> m_fn;
};
int main() {
// Create a thing object
thing t;
// In a limited scope
{
// Local lambda
auto afn = []{std::cout << "hi\n";};
// Store the lamda by reference
t.register_fn(afn);
}
// Run the stored lambda (which should be destroyed - therefore dangling reference??)
t.run();
// Take a copy
thing t2 = t;
t2.run();
return 0;
}
在此处查看 运行:https://godbolt.org/z/6qW3ro
所以,我有一个 class 存储通过引用传递的临时 lambda。 lamda afn
的范围是有限的,因此一旦它被传递给 register 函数,它就会超出范围。然后在这个范围之外,我调用 run
函数,它应该是 运行 对 lambda 的(悬空?)引用。
这一直有效,但最近回顾我的代码我有疑问。由于 lambda 是临时的(在这里通过限制我的 lambda 对象 afn
的范围来完成) - 这不应该起作用......我无法完全理解它为什么起作用 - 除非运气好,这是未定义的行为?
或者...我在这里误解了什么? - 因为这可能是最有可能的解释!
这主要是因为您复制了函数对象。在
void register_fn(const std::function<void()> &fn)
{
m_fn = fn;
}
你将 fn
赋值给 m_fn
来制作副本,即使 fn
是对本地 lambda 的引用,制作副本也意味着 m_fn
不会引用 fn
,但会得到存储在其中的函数 fn
的副本。这意味着没有悬空引用并且您的代码具有明确定义的行为。
如果 lambda 通过引用捕获本地对象,情况会有所不同,因为在您离开声明 lambda 的范围后,该捕获将失效。