8

根据我的经验,似乎:

  • 在函数调用中创建的 lambda 表达式在调用后立即销毁
  • 调用期望 a 的函数std::function会从 lambda 中创建一个临时对象 (std::function),并且该对象在调用后被销毁

可以通过以下代码片段观察到此行为:

const function<void()>* pointer;

void a(const function<void()> & f)
{
    pointer = &f;
}

void b()
{
    (*pointer)();
}

int main()
{
    int value = 1;
    std::cout << &value << std::endl;

    // 1: this works    
    function<void()> f = [&] () { std::cout << &value << std::endl; };
    a(f);

    // 2: this doesn't
    a([&] () { std::cout << &value << std::endl; });

    /* modify the stack*/
    char data[1024];
    for (int i = 0; i < 1024; i++)
        data[i] = i % 4; 

    b();

    return 0;
}

在第二种情况下到底发生了什么?是否有正确的调用方法a()而不创建显式std::function对象?

编辑: : 这两个版本(1 和 2)编译得恰到好处,但会产生不同的输出:

版本 1:

0x7fffa70148c8
0x7fffa70148c8

版本 2:

0x7fffa70148c8
0
4

3 回答 3

3

如果您创建一个临时文件,它将在行尾消失。正如您正确指出的那样,这意味着存储指向它的指针是一个坏主意。

如果要存储指向 a std::function(或其他任何东西)的指针,则需要确保在停止使用指针之前它的生命周期不会结束。这意味着您确实需要一个类型为 的命名对象std::function

至于在第二种情况下发生了什么:您创建一个临时 lambda 以传递给函数。由于该函数需要 a ,因此将从 lambda 创建std::function一个临时值。std::function这两个都将在行尾被销毁。因此,您现在有一个指向已经销毁的临时对象的指针,这意味着尝试使用指向的对象会将您牢牢地带入未定义的行为领域。

于 2012-12-09T15:20:31.567 回答
1

无状态 lambdas没关系。无状态 lambda 具有到函数指针类型的隐式转换。

此外,无论实际的可调用类型如何,总是存在到 std::function<> 的隐式转换。

但是,保留指向临时对象的指针存在问题。我在第一次阅读代码时没有注意到这一点。

当然,这与 std::function 无关。

于 2012-12-09T15:06:31.380 回答
0

临时对象在创建它们的语句结束时被销毁。您刚刚使用 lambda 演示了这一点。

你第一次传递的std函数不是临时的,所以它和变量一样长。它实际上存储了临时的副本。

要让您的 lamdas 持续超过一条线,您需要将其存储在某个地方并确定其使用寿命。

有很多方法可以做到这一点。标准函数是一种基于类型擦除的存储方式——即,存储标准函数而不是指向标准函数的指针。将类型更改pointer为非指针(和非常量),在分配给它时去掉&它,然后直接调用它。

通常避免将 const 引用函数参数的地址作为临时绑定来获取和存储,除非您在另一个重载中发现右值引用的可能性。

于 2012-12-09T16:05:00.377 回答