为什么捕获 lambda 在 C++ 中不起作用?

Why capture lambda does not working in c++?

我正在使用 C++ 中的 lambda 表达式,我尝试了一些方法来查看结果。我实际上在 CppCon Back to Basics: Lambdas from Scratch - Arthur O'Dwyer - CppCon 2019 @21:47 观看了视频并开始使用 lambdas。

举个例子,我试过这个:

#include <iostream>
using namespace std;
int g = 10;//global var 'g'

//creating lambda
auto kitten = [=] () {return g+1;};
auto cat = [g=g] () {return g+1;};
// main
int main()
{
    g = 20;//modifying global variable 'g'
    cout<<"kitten: "<<kitten()<<"cat: "<<cat()<<endl;

    return 0;
}

以上代码的输出为:

kitten: 21cat: 11

在上面的例子中:[g=g]表示捕获一个名称为g且类型与外部g相同的数据成员,就好像我写了auto g=g。这是 g 的副本。当我们认为(就像我以 auto g=g 的形式写的一样)所以结果在我们的例子中是 11 时,这是有道理的,其中全局 g 的修改没有反映在我们的 [=21 中=].

小猫的结果是 21 因为据我所知,捕获所有内容,即按值捕获所有外部变量。

然后,当涉及到这个例子时,修改第一个lambda如下:

auto kitten = [] () {int g  = g; return g+1;};

在我声明 local g 并从 global g 赋值的地方,输出是:

kitten: 1cat: 11

但我期待第一个示例 (21) 中的输出,因为我正在尝试创建本地 g 并从全局 g 分配它的值,它已经是修改值为 20.

代码是在 https://techiedelight.com/compiler/ 和 godbolt.org 上用 c++ (GCC 8.3.0) 编译的(最新的编译器,[=] 这是不允许的,但结果是一样的).

此时此刻,我对捕获and/or lambda的概念有点困惑。

auto kitten = [] () {int g  = g; return g+1;};

您在这里根本没有使用全局变量。您正在使用本地 g 来初始化本地 g。程序的行为未定义。

why int g = g; trying to initialize local g by itself,

因为初始化器在声明局部 g 的点之后。

shouldn't that compiler initialize with the global g?

没有

auto kitten = [=] () {return g+1;}

这个 lambda 根本没有捕获任何东西。几乎与

相同
int kitten() { return g+1; }

只能捕获局部变量,在kitten定义的范围内没有可见的局部变量。请注意,[=][&] 并不意味着“捕获一切”,它们意味着“捕获任何必要的东西”,并且全局变量永远不需要(或不可能)在 lambda 中捕获,因为含义无论何时评估 lambda 主体,该变量名称的名称始终相同。


auto cat = [g=g] () {return g+1;}

这里有一个init-capture,类似于创建一个局部变量并立即捕获它。等号前的g声明了init-capture,等号后的g指定了如何初始化。与大多数声明符(见下文)不同,此处创建的 g 变量不在其自身初始化程序的范围内,因此等号后的 g 表示全局变量 ::g。所以代码类似于:

auto make_cat()
{
    int & g = ::g;
    return [g]() { return g+1; }
}
auto cat = make_cat();

auto kitten = [] () {int g  = g; return g+1;}

此代码有一个与 lambda 无关的错误。在局部变量定义 int g = g; 中,等号之前声明的变量在等号之后的初始化期间处于范围内。所以 g 被初始化为它自己的不确定值。向该不确定值加一是未定义的行为,因此结果不可预测。