局部变量的 C++ lambda 词法闭包

标签 c++ c++11 lambda

总结

在 C++ 中,当我从捕获该函数局部变量的函数返回 lambda 时,具体会发生什么,为什么?编译器 (g++) 似乎允许这样做,但它给我的结果与我预期的不同,所以我不确定这在技术上是否安全/受支持。

详情

在某些语言(Swift、Lisp 等)中,您可以在闭包/lambda 中捕获局部变量,只要闭包在范围内,它们就保持有效并在范围内(我听说它称为“lambda over在 Lisp 上下文中放弃 lambda”)。例如,在 Swift 中,我尝试做的示例代码是:

func counter(initial: Int) -> (() -> Int) {
    var count = initial
    return { count += 1; return count }
}

let c = counter(initial: 0)
c() // returns 1
c() // returns 2
c() // returns 3

我尝试编写一个 C++ 等效项,如下所示:

auto counter(int initial)
{
    int count = initial;
    return [&count] () -> int {
        count = count + 1;
        return count;
    };
}

然而,我得到的结果是:

auto c = counter(0);
std::cout << c() << std::endl; // prints 1
std::cout << c() << std::endl; // prints 1
std::cout << c() << std::endl; // prints 1

如果我捕获了一个仍在范围内的变量,它会按我预期的那样工作。例如,如果我在一个函数中执行以下所有操作:

int count = 0;
auto c = [&count] () -> int {
    count = count + 1;
    return count;
};
std::cout << c() << std::endl; // prints 1
std::cout << c() << std::endl; // prints 2
std::cout << c() << std::endl; // prints 3

所以我想我的问题是,在上面的第一个 C++ 示例中,实际上捕获了什么?它是定义的行为,还是我只是引用了堆栈上的一些随机内存?

最佳答案

    return [&count] () -> int {

这是引用捕获。 lambda 捕获对此对象的引用。

有问题的对象 count 在函数的本地范围内,因此当函数返回时,count 被销毁,this 成为对对象的引用超出范围并被销毁。使用此引用成为未定义的行为。

按值捕获似乎可以解决这个问题:

    return [count] () -> int {

但您的明显意图是让此 lambda 的每次调用都返回一个单调递增的计数器值。仅按值捕获对象是不够的。您还需要使用 mutable lambda :

 return [count] () mutable -> int
 {
    return ++count;
 };

但是对你的问题“发生了什么”的迂腐回答是,lambda 本质上是一个匿名类,而 lambda 捕获的是真正的类成员。你的 lambda 相当于:

class SomeAnonymousClassName {

     int &count;

public:
     SomeAnonymousClassName(int &count) : count(count)
     {}

     int operator()
     {
          // Whatever you stick in your lambda goes here.
     }
};

通过引用捕获某些内容会转换为作为引用的类成员。按值捕获某些东西转化为不是引用的类成员,捕获 lambda 变量的行为转化为将它们传递给 lambda 类的构造函数,这就是创建 lambda 时发生的情况。 lambda 实际上是匿名类的实例,具有已定义的 operator()

在常规的 lambda 中,operator() 实际上是一个 const 运算符方法。在可变的 lambda 中,operator() 是一个非 const,一个可变的运算符方法。

关于局部变量的 C++ lambda 词法闭包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41326907/

相关文章:

c++ - 如何在 C++ 中将文本文件读入二维动态 vector ?

c++ - 如何在运行时检查 c++11 元组是否有可能的函数应用程序

c++ - 在 C++ 中使用 erase-remove 习语时,我应该通过引用传递吗?

java - JavaFX 中不使用 Lambda 表达式的自动完成组合框

java - 当再次处理相同的数据和任务时,Java 8 流是否会重用自身?

c++ - 显示图像 Sprite 中的特定图像

c++ - 使用 Media Foundation SDK 进行直播

c++ - LLVM Clang C++ 代码注入(inject)

c++ - 从 std::map 中删除 std::function lambda-wrapped 方法

python - 嵌套列表结构中的聚合数据