在下面的代码中,我创建了一个通过引用捕获本地变量的lambda。请注意,它是一个指针,因此,如果C++ lambda是真正的闭包,它应该在创建lambda的函数的生命周期内存活。
然而,当我再次调用它时,它不会创建新的本地变量(新环境),而是重复使用先前的变量,并且实际上捕获了与之前完全相同的指针。
这似乎是错误的。要么C++ lambda不是真正的闭包,要么我的代码有误?
感谢您的帮助。
这段代码返回:
但是我期望它会返回:
如果 C++ Lambda 是真正的闭包,那么每个局部计数器将与返回的函数共存(该函数携带其环境——至少是部分环境)。但事实上,调用 create_counter 后计数器被销毁,调用返回的函数会导致段错误。这不是闭包的预期行为。
Marco A 建议一种解决办法:通过复制传递指针。这增加了引用计数器,因此在 create_counter 后它不会被销毁。但这只是一个折中办法。但正如 Marco 指出的那样,它能够正常工作并且完全符合我的期望。
Jarod42 建议声明变量并将其初始化为捕获列表的一部分。但这破坏了闭包的目的,因为这些变量现在是函数局部变量,而不是创建函数时的环境变量。
apple apple 建议使用静态计数器。但这是一种避免在 create_function 结束时销毁变量的解决办法,并且意味着所有返回的函数共享同一个变量,而不是运行环境下的环境变量。
因此,我猜结论是(除非有人能提供更多信息)C++ 中的 Lambda 不是真正的闭包。
感谢您的评论。
然而,当我再次调用它时,它不会创建新的本地变量(新环境),而是重复使用先前的变量,并且实际上捕获了与之前完全相同的指针。
这似乎是错误的。要么C++ lambda不是真正的闭包,要么我的代码有误?
感谢您的帮助。
#include <iostream>
#include <functional>
#include <memory>
std::function<int()> create_counter()
{
std::shared_ptr<int> counter = std::make_shared<int>(0);
auto f = [&] () -> int { return ++(*counter); };
return f;
}
int main()
{
auto counter1 = create_counter();
auto counter2 = create_counter();
std::cout << counter1() << std::endl;
std::cout << counter1() << std::endl;
std::cout << counter2() << std::endl;
std::cout << counter2() << std::endl;
std::cout << counter1() << std::endl;
return 0;
}
这段代码返回:
1
2
3
4
5
但是我期望它会返回:
1
2
1
2
3
进一步编辑:
感谢您指出我原始代码中的错误。现在我明白了,发生的情况是在调用create_couter后指针被删除,新的create只是重复使用同一块内存地址。
那么这就带来了我真正的问题,我想要做的是:
std::function<int()> create_counter()
{
int counter = 0;
auto f = [&] () -> int { return ++counter; };
return f;
}
如果 C++ Lambda 是真正的闭包,那么每个局部计数器将与返回的函数共存(该函数携带其环境——至少是部分环境)。但事实上,调用 create_counter 后计数器被销毁,调用返回的函数会导致段错误。这不是闭包的预期行为。
Marco A 建议一种解决办法:通过复制传递指针。这增加了引用计数器,因此在 create_counter 后它不会被销毁。但这只是一个折中办法。但正如 Marco 指出的那样,它能够正常工作并且完全符合我的期望。
Jarod42 建议声明变量并将其初始化为捕获列表的一部分。但这破坏了闭包的目的,因为这些变量现在是函数局部变量,而不是创建函数时的环境变量。
apple apple 建议使用静态计数器。但这是一种避免在 create_function 结束时销毁变量的解决办法,并且意味着所有返回的函数共享同一个变量,而不是运行环境下的环境变量。
因此,我猜结论是(除非有人能提供更多信息)C++ 中的 Lambda 不是真正的闭包。
感谢您的评论。