通过 const std::function 引用传递临时 lambda 应该会失败,但似乎有效

passing a temporary lambda by const std::function reference should fail but seems to work

提问人:code_fodder 提问时间:9/30/2020 更新时间:9/30/2020 访问量:93

问:

这是我正在做的事情的简化版本:

#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

因此,我有一个类来存储通过引用传递的临时 lambda。lamda 的范围是有限的,因此一旦它被传递给寄存器函数,它就会超出范围。然后,在此范围之外,我调用函数,该函数应该运行对 lambda 的(悬空?)引用。afnrun

这一直在工作,但最近回顾我的代码,我有一个疑问。由于 lambda 是临时的(这里通过限制我的 lambda 对象的范围来完成) - 这应该不起作用......我不太明白它为什么会起作用——除非运气好,这是未定义的行为?afn

或。。。我在这里误解了什么?- 因为这可能是最可能的解释!

C++ C++11 按引用传递

评论

0赞 code_fodder 9/30/2020
哎呀......等等 - 我想我只是混淆了自己 - 我是通过参考,但我在存储它时会拿一份副本......最好等你们确认一下!:o
1赞 songyuanyao 9/30/2020
是的,它是在 .m_fn = fn;
0赞 code_fodder 9/30/2020
@songyuanyao phew - 谢谢:)我会结束这个问题,但我对 Scheff 有一个疑问......
1赞 code_fodder 9/30/2020
我投票结束这个问题,因为我只是感到困惑......
1赞 Scheff's Cat 9/30/2020
仅供参考:我忽略了 lambda 本身不是 lambda,而是被包装在后者的实例中。该实例的生存期确实有限......std::functionstd::function

答:

1赞 NathanOliver 9/30/2020 #1

这主要是因为您制作了函数对象的副本。在

void register_fn(const std::function<void()> &fn)
{
    m_fn = fn;
}

你赋给哪个 make a copy 和 即使是对本地 lambda 的引用,make a copy 意味着不引用 ,而是会得到一个函数的副本已经存储在其中。这意味着没有悬空引用,并且您的代码具有明确定义的行为。fnm_fnfnm_fnfnfn

如果 lambda 通过引用捕获本地对象,则情况会有所不同,因为在您离开声明 lambda 的作用域后,该捕获将变得无效。

评论

1赞 Deduplicator 9/30/2020
顺便说一句,会更好。void register_fn(std::function<void()> fn) noexcept { m_fn = std::move(fn); }
0赞 code_fodder 9/30/2020
@Deduplicator ah std::function 有一个移动 c'tor - 好点 :)