简介
lambda表达式是C++11中引进的新特性之一,也是最常用的特性之一。lambda表达式来源于函数式编程的概念,也是现代编程语言的一个特点。
lambda表达式具有以下特点:
1. 不需要额外再写一个函数或者函数对象。
2. 就地就可以匿名地定义一个目标函数或者函数对象。
3. 在STL中用处很多。
基本语法和使用
在C++中可以调用的对象有下面四种:
- 函数
- 函数指针
- 函数对象(重载了函数调用运算符的类)
lambda表达式
一个lambda表达式就是一个可调用的代码单元。可以将其理解为一个未命名的内联函数。与任何函数类似,lambda表达式有返回类型、参数列表和函数体,但是不同的是lambda表达式可能定义在函数内部(函数不能在函数内部定义)。
lambda表达式的语法形式:
[捕获列表] (参数列表) -> 返回值类型 {函数体}
其中,捕获列表是一个lambda所在函数中定义的局部变量的列表,其他和普通函数类似。
我们可以忽略参数列表和返回值类型,但是必须永远包含捕获列表和函数体。因此一个完整的lambda表达式看起来像这样:
auto f = [] (int a) -> int { return a + 1; };
std::cout << f(1) << endl; //输出: 2
当省略lambda表达式的返回值类型时,当函数体中只有return语句时,编译器会自动推断返回值类型,但是函数体中除了return语句之外还有其他的语句后,编译器默认为返回void类型。
lambda表达式可以用在STL中的谓词参数中。
stable_sort(words.begin(), words.end(),
[] (const string &a, const string &b)
{ return a.size() < b.size(); });
当stable_sort
需要比较两个元素时,就会调用给定的lambda表达式。
lambda的捕获列表
lambda表达式可以出现在函数中,使用其局部变量,但是他只能使用那些明确指明的变量。通过捕获列表来获得这些指明的变量,捕获列表指引lambda在其内部包含访问局部变量所需要的信息。
值捕获
值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝,并且由于被捕获的变量是在lambda创建时的拷贝,因此随后的修改不会影响到lambda内对应的值。
void fcn1()
{
size_t v1 = 42; //局部变量
//将v1拷贝到名为f的可调用对象
auto f = [v1] { return v1; };
v1 = 0;
auto j = f(); //j为42,f保存了我们创建它时v1的拷贝
}
引用捕获
引用捕获方式捕获的变量在lambda内使用的时候是引用所绑定的对象。
void fcn2()
{
size_t v1 = 42; //局部变量
//对象f2包含v1的引用
auto f2 = [&v1] { return v1; };
v1 = 0;
auto j = f(); //j为0,f2保存了v1的引用,而非拷贝
}
注意:如果我们采用引用方式捕获一个变量,就必须保证被引用的对象在lambda执行的时候是存在的。
隐式捕获
让编译器根据lambda体中的代码推断使用哪些变量。
[=] 捕获外部作用域中的所有变量,并作为副本在函数体中使用
[&] 捕获外部作用域中的所有变量,并作为引用在函数体中使用
[=, &foo] 按值捕获外部作用域中的所有变量,并按引用捕获foo变量
可变lambda
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值,如果希望能改变被捕获的值,就必须在参数列表首加上关键字mutable
void fcn3()
{
size_t v1 = 42; //局部变量
//f可以改变它所捕获的变量的值
auto f = [v1] { return ++v1; };
v1 = 0;
auto j = f(); //j为43
}
一个引用捕获的变量是否可以修改,依赖于此引用指向的是一个const类型还是一个非const类型:
void fcn3()
{
size_t v1 = 42; //局部变量
// v1是一个非const变量的引用
// 可以通过f2的引用来改变它
auto f2 = [&v1] { return ++v1; };
v1 = 0;
auto j = f2(); //j为1
}