Lambda 表达式和函数指针
1. 什么是Lambda表达式?
C++11引入了Lambda表达式,它是一种用于创建匿名函数的方法。Lambda表达式在编写更简洁、清晰的代码时非常有用,特别是在需要传递函数作为参数的情况下,比如STL算法、自定义排序函数、回调等。
Lambda表达式的形式
Lambda表达式的一般形式如下:
[捕获列表](参数列表) mutable-> 返回类型 {
// 函数体
}
让我们详细讲解Lambda表达式的各个部分以及如何使用它们:
- 捕获列表(Capture List) :Lambda表达式可以在其内部访问外部作用域的变量。捕获列表用于指定Lambda表达式需要捕获哪些外部变量以供使用。(不能省略)
捕获列表有三种形式:[]
:不捕获任何变量。[变量名]
:捕获指定变量(复制捕获)。
[&变量名]
:引用捕获,以引用方式访问变量(引用捕获)。[=]
:以复制捕获方式捕获所有外部变量。[&]
:以引用捕获方式捕获所有外部变量。
- 参数列表(Parameter List) :与普通函数类似,如果不需要参数传递,则可以连同()一起省略,用于指定Lambda表达式的参数列表。
- 返回类型(Return Type) :可以省略,编译器可以自动推断返回类型。如果存在返回语句,编译器会根据返回语句推断返回类型。如果Lambda表达式中包含多条语句,且需要指定返回类型,可以使用
->
符号。 - mutable : 默认情况下,lambda总是一个const函数,mutable可以取消其常量性,使用该修饰符时,参数列表不可省略(即使参数为空)。mutable放在参数列表和返回值之间。
- 函数体(Function Body) :Lambda表达式的具体实现,包含要执行的代码(不能省略)。
在lambda函数定义中,参数列表和返回值都是可选部分,而捕获列表和函数体可以为空。C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情,没有意义。
Lambda表达式的隐式捕获
除了显式列出我们希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体中的代码来推断我们要使用哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用捕获引用方式,=则表示采用值捕获方式。例如:
//sz为隐式捕获,值捕获方式
auto wc = find_if(words.begin(),words.end(),[=](const string &s)
{
return s.size() >= sz;});
隐式捕获和显示捕获也可以混合使用,如下代码:
void elimDups(vector<string>& words)
{
//对words按照字典序进行升序排序
sort(words.begin(),words.end());
//将重复的字符移动至words后半部分,并返回前半部分最后一位的下一位的迭代器
auto end_unique=unique(words.begin(),words.end());
//删除后部分的内容
words.erase(end_unique,words.end());
}
void biggies(vector<string> &words,vector<string>::size_type sz,ostream &os,char c = ' ')
{
//按照字典序排序,去重
elimDups(words);
//按照字符串长度升序排序,当长度相同时,维持字典序
stable_sort(words.begin(),words.end(),
[](const string& a,const string& b)->bool
{
return a.size() < b.size();});
//返回第一个长度大于等于sz的迭代器
auto wc=find_if(words.begin(),words.end(),[sz](const string& s)->bool
{
return s.size()>=sz;});
//os 隐式捕获,引用捕获,c显示捕获,值捕获
for_each(words.begin(),words.end(),[&,c](const string &s){
os<<s<<c;});
//os显示捕获,引用捕获,c隐式捕获,值捕获
for_each(words.begin(),words.end(),[=,&os](const string &s){
os<<s<<c;});
}
当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=。此符号指定了默认捕获方式为引用或值。且当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。
可变Lambda
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable。
void fcn3()
{
size_t v1=42;
//通过mutable,f可以改变它所捕获的值
auto f = [v1]() mutable{
return ++v1;};