Lambda捕获意外变量

时间:2016-05-16 23:08:15

标签: c++ lambda

我试图弄清楚lambda如何在C ++中运行。

发生了一件奇怪的事。很奇怪,我不知道如何正确描述它。我尝试使用谷歌搜索几个关键字,但没有找到任何提及行为的内容。

我首先尝试this code

#include <iostream>
#include <utility>
using namespace std ;

auto func() {
    int a = 0 ;

    auto increase = [ &a ]( int i = 1 ){ a += i ; } ;
    auto print = [ &a ](){ cout << a << '\n' ; } ;

    pair< decltype(increase), decltype(print) > 
    p = make_pair( increase, print ) ;
    return p ;
}

int main() {
    auto lambdas = func() ;

    auto increase = lambdas.first ;
    auto print = lambdas.second ;

    print() ;
    increase() ;
    print() ;
    increase( 123456 ) ;
    print() ;

    return 0;
}

输出符合预期

-1218965939
-1218965938
-1218842482

然而,在我将其添加到&#39; func()&#39;之后

cout << typeid( decltype( print ) ).name() << '\n'
     << typeid( decltype( increase ) ).name() << '\n' ;

喜欢this one

输出成为

Z4funcvEUlvE0_
Z4funcvEUliE_
0
1
123457

我没想到会发生。

[更新]

变量a应该是&#34;死&#34;因为它的生命周期已经结束。

但我很好奇为什么代码考试typeiddecltype导致a似乎复活了?

3 个答案:

答案 0 :(得分:4)

您通过引用绑定到a。但这是一个存储在堆栈中的局部变量。一旦函数完成执行,它就是未定义的行为。

与返回指向a的指针然后从调用者开始使用它的情况相同。

答案 1 :(得分:3)

您的计划的所有输出都不是“按预期”。

func()中的lambdas通过引用捕获一个本地范围的变量,一旦func()返回就会超出范围。

func()返回后,a不再存在,就像任何其他函数本地范围对象一样。因此,他们捕获的引用现在被引用到超出范围并被销毁的对象,并且引用值的任何使用都变为未定义的行为。

更糟糕的是,代码还通过不再有效的引用设置值。在传统的实现中,这会乱写堆栈的一些随机部分,这可能导致整个过程崩溃。

答案 2 :(得分:2)

纯粹的机会。

我怀疑你知道,你是通过悬空参考打印未指定的值。

  • 在您的第一个示例中,悬空引用尝试从已经重新用于其他内容的内存位置“读取”。

  • 在您的第二个示例中, cout和/或typeid已经影响了编译程序实现的血腥胆量,使得内存a的位置恰好在您非法打印其价值时不受影响。

但是,在尝试进一步理解这一点时,没有意义,并且下次运行程序时可能会得到不同的结果。或者您的计算机可能爆炸。或者可以改变时间表,使你从未出生过。 不要试图解释UB的症状 - 只要避免它。