在C++中访问lambda捕获初始化变量之外的lambda表达式

9
在C++14/17中,如何在lambda作用域之外访问初始化的变量?
来源:
#include <iostream>

using namespace std;

int main(){
    auto test = [value1 =0]() mutable {value1+=1; return value1;};
    cout << test() << endl;
    cout << test() << endl;
    //cout << value1 << endl;//error: ‘value1’ was not declared in this scope
}

输出:

1

2

value1变量是否可以在test()lambda的作用域之外访问?lambda捕获初始化变量的生命周期是什么?

尝试在lambda之外访问value1会出现以下错误:error: ‘value1’ was not declared in this scope

使用gcc版本7.3.0编译(Ubuntu 7.3.0-21ubuntu1~14.04)。


2
你不能这样做。它的作用域限定在 lambda 内部。 - T.C.
2个回答

9
一个 lambda 只是内联定义结构体和这个结构体上的 operator() 重载的紧凑定义 (以及创建该结构体类型对象)。Lambda 的 "捕获" 只是这个结构体的成员变量,由类型的构造函数初始化。这就是为什么 C++ lambda 必须具有按值与按引用捕获的语法的原因之一。
但是结构体的成员变量是私有的。由于编译器生成的结构体在很大程度上是实现定义的,标准并不要求使用这些名称公开这些成员。如果编译器生成的结构体想要使用其他名称,编译器将只需重新映射对这些名称的内部使用以引用成员的名称即可。
所以,不,lambda 捕获的任何类型都不能被 lambda 外部的世界访问。如果捕获一个对象的引用,则外部世界可以访问相同的对象。但您将无法访问指向该对象的相同引用。

感谢解释。我之前没有意识到 lambda 捕获初始化变量是结构体的私有成员。 - Eugene

5

为了完整性,根据编译器的不同,技术上可以访问lambda的内部成员并进行修改。尽管这基本上是利用了实现细节,但绝不能这样做。但它确实提供了一些有关lambda实现的见解。

以下是GCC 6.3中的示例。

#include <iostream>

using namespace std;

template<typename Lambda>
struct lambda_member : Lambda
{

    Lambda& f_;
    lambda_member(Lambda& f) : Lambda(f),
        f_(f)
    {}

    auto& get_value1()
    {
        return f_.__value1;
    }

};


int main(){
    auto test = [value1 =0]() mutable {value1+=1; return value1;};

    lambda_member<decltype(test)> lm{test};

    std::cout << test() << std::endl;
    std::cout << lm.get_value1() << std::endl;
    lm.get_value1() = 22;
    std::cout << test() << std::endl;

}

Demo


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接