通过 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 的范围后,该捕获将失效。