在编程语言中,闭包(closure),又称为词法闭包(lexical closure)或函数闭包(function closure),是一种在具有一流函数的语言中(a language with first-class functions)实现词法作用域名称绑定的技术。从操作上来说,闭包是一个将函数与环境一起存储的记录。环境是一个映射,将函数的每个自由变量(free variable)(在本地使用但在封闭范围内定义的变量)与创建闭包时名称绑定的值或引用相关联。与普通函数不同,闭包允许函数通过闭包的值或引用的副本来访问这些捕获的变量,即使在函数作用域之外调用该函数也是如此。
闭包是编程中的一个通用概念,起源于函数式编程。谈论C++中的闭包时,总是带有lambda表达式。
1.在C++中,lambda表达式是用于创建特殊临时对象的语法,其行为类似于函数对象的行为。
2.C++标准特意将这种类型的对象称为闭包对象(closure object)。这与闭包的更广泛定义有点不一致,闭包指的是从定义变量的环境中捕获变量的任何函数,无论是否匿名。
3.就标准而言,所有lambda表达式的实例化都是闭包对象,即使它们的捕获组中没有任何捕获。
在任何其他语言(例如Python)中,闭包与lambda无关.
C++中的闭包:由lambda表达式定义(而不是封装)的值(value),该表达式由代码以及代码中引用的变量的值组成。所以闭包是一个匿名函数对象,由编译器作为lambda表达式的结果自动创建。闭包存储lambda表达式中使用的lambda表达式定义范围内的这些变量。
lambda表达式的运行时效果是生成对象。此类对象称为闭包。(The runtime effect of a lambda expression is the generation of an object. Such objects are known as closures)
闭包是一个通过引用其主体外部的字段来封闭其周围状态的函数。封闭状态在闭包调用中保持不变。
lambdas vs. Closures for C++:lambda和相应的闭包之间的区别完全等同于类和类实例之间的区别。
闭包之于lambda就像对象之于类一样(Closures are to lambdas as objects are to classes)。
类只存在于源代码中;它在运行时不存在。运行时存在的是类类型的对象。同样地:每个lambda表达式都会导致生成一个唯一的类(在编译期间),并且还会导致创建该类类型的对象(闭包)(在运行时)。(As we know, a class exists only in source code; it doesn’t exist at runtime. What exists at runtime are objects of the class type. Similarily: Each lambda expression causes a unique class to be generated(during compilation) and also causes an object of that class type--a closure--to be created(at runtime))
(1).例如,lambda在运行时不占用数据内存,尽管它们可能占用代码内存。
(2).闭包占用数据内存,但不占用代码内存。
注:以上内容主要翻译自:medium.com
以下为测试代码:
namespace {
std::function<void(void)> closureWrapper1()
{
int x = 10;
return [&x]() { std::cout << "Value in the closure: " << x++ << std::endl; };
// return [x]() mutable { std::cout << "Value in the closure: " << x++ << std::endl; };
}
} // namespace
int test_closure()
{
// reference: https://pranayaggarwal25.medium.com/lambdas-closures-c-d5f16211de9a
int fudgeFactor{ 6 };
// "="右侧的表达式是lambda表达式,该表达式创建的运行时对象是闭包
// f本身不是闭包,它是闭包的副本
// 将闭包复制到f的过程可以优化为移动(move),但这并不能改变f本身不是闭包的事实
auto f = [&](int x, int y) { return fudgeFactor * (x + y); };
// 实际的闭包对象是一个临时对象,通常会在语句末尾被销毁,除非将其绑定到转发引用(forwarding reference)(也称为通用引用(universal reference))
// 或左值引用到常量(lvalue-reference-to-const)
auto&& rrefToClosure = [&](int x, int y) { return fudgeFactor * (x + y); };
const auto& lrefToConstToClosure = [&](int x, int y) { return fudgeFactor * (x + y); };
int x = 10;
auto func0 = [&x]() { x += 1; std::cout << "Value in the closure: " << x << std::endl; };
// func0是由其后编写的lambda表达式创建的闭包的副本
func0(); // Value in the closure: 11
// func1不是闭包而是一个包装闭包的std::function包装对象
std::function<void(void)> func1 = closureWrapper1();
func1(); // Value in the closure: 32758 // prints garbage value + 1 =~ garbage value
return 0;
}
执行结果如下图所示:linux下调用func1现象是段错误
GitHub:https://github.com/fengbingchun/Messy_Test