c++ 11 lambda -为什么我需要捕获自动持续时间变量

C++11 lambda - why do I need to capture auto duration variables?

本文关键字:变量 持续时间 lambda 为什么 c++      更新时间:2023-10-16

当在捕获'[]'部分编写lambda函数时,我需要只指定自动持续时间变量,而全局和静态变量在lambda函数中使用而不需要捕获。为什么呢?为什么我们不能像使用全局和静态变量一样使用自动持续时间变量?

因为lambda定义了一个单独的作用域:它相当于:

int global_i;
struct Lambda {
    Lambda(int captured_j) : captured_j(captured_j) {}
    void operator()(){
        // in this scope, global_i is accessible, and by capturing auto_j, we make that visible as well, but we can't see auto_i
    }
    int captured_j;
}
void foo() {
    int auto_i;
    int auto_j
    // this lambda
    [j](){}
    // is just shorthand for this:
    Lambda lambda(j);
}

lambda被编译器转换成一个函数对象(就像我的例子中的Lambda类)。并且函数对象不能访问在函数对象实例化的作用域中声明的局部变量。除非你通过将它们传递给构造函数来"捕获"它们。

至于为什么编译器不隐式地这样做,不需要你提示它,因为它不知道你是想按值捕获还是按引用捕获。在GC语言中,您总是通过引用捕获,因为这无关紧要——只要您需要,您引用的对象就会一直存在。

但是在c++中,您需要管理这些对象的生命周期,如果lambda总是通过引用捕获,那么它在声明它的作用域之外实际上是无用的(因为它将包含对已被销毁的对象的引用)。因此,在c++中,必须指定是要按值捕获还是按引用捕获。因为你必须指定,编译器不能直接为你做

考虑这个简单的函数:

std::function<foo()> f()
{
    foo afoo;
    return [=](){ return afoo; };
}
int main()
{
    auto l = f();
    l();
}

如果不捕获变量afoo,那么在lambda形成的闭包被使用之前,它就会超出作用域!

还请注意,我使用按值捕获完全是出于同样的原因:返回指向局部变量的引用/指针具有未定义的行为。

实际上,lambda并没有什么神奇之处;简单地把它看作一个匿名函子。

传统的函子也不知道它所在的局部变量,你需要把它们提供给它的函数。像lambda这样的匿名对象没有可自定义的c'tor,因此您可以通过这种"[]"语法提供上下文。

或者考虑

const float g_foo = 42.f;
struct A {
    float _bar;
    struct B {
        B() : _baz(_bar /*oops*/) {}
        float _baz;
    };
};

在A::B的闭包中没有A的"_bar"的概念,但是g_foo是已知的。