C++ 中的 Lambda 表达式
一个Lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数,可以在需要函数对象的地方直接定义和使用;与任何函数相似,一个lambda表达式具有一个返回类型,一个参数列表,一个函数体,但与函数不同,lambda表达式可以定义在函数内部。
Lambda表达式: 1.可以就地匿名定义目标函数或函数对象,不需要额外写一个函数 2.lambda是一个匿名的内联函数
lambda表达式本质是一个匿名函数,可以用函数指针来接收
作用是:提供一种简洁的方式来定义小型的,局部的函数,特别是在使用算法和函数式编程风格时十分方便。
Lambda表达式的语法:
[capture list捕获列表](parameter list参数列表) ->return 返回值类型{函数体}
捕获列表用于捕获外部变量,参数列表和返回类型与普通函数类似,函数体包含具体的操作。我们可以忽略参数列表和返回类型,但永远包含捕获列表和函数体,其中捕获列表内容通常为空。
Lambda表达式可以捕获局部变量,成员变量,全局变量
捕获方式有值捕获(按值复制外部变量),引用捕获(通过引用捕获外部变量)和混合捕获(同时使用值捕获和引用捕获)
捕获列表[]:捕获一定范围内的变量
参数列表(): 和普通函数的参数列表一样,如果没有参数参数列表可以省略不写
捕获列表
[ ] 不捕获任何变量
[&] 捕获外部作用域中的所有变量,并且按照引用捕获,引用捕获可读可写(修改)
[=]捕获外部作用域的所有变量,按照值捕获,拷贝过来的副本在函数体内是只读的
[= ,&a] 按值捕获外部作用域中的所有变量,并且按照引用捕获外部变量 a
[bar] 按值捕获bar变量,不捕获其他变量
[this] 捕获当前类中的this指针(this指针在类内成员函数里,静态成员函数不含this指针),让lambda表达式拥有和当前类成员函数同样的访问权限
一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值类型,但是需要注意的是lambda表达式不能通过列表初始化自动推导出返回值类型。
void fun()
{
//lambda可以用函数指针来接收
int(*p)() = []()->int { cout << "hello!" << endl; return 0; };//匿名函数 lambda
p();//p是一个函数指针,指向函数的首地址,加()可以访问该元素
//lambda可以用auto 变量名来接收
auto f = []()-> int { cout<<"abc"<<endl; return 0; };
f();
//当没有函数参数时,()可以省略,当返回值类型是int时,int也可以省略,函数会自动识别返回值类型
int(*w)() = [] { cout << "得分" << endl; return 0; };
w();
}
int main()
{
fun();
return 0;
}
//不能在函数内部访问另一个函数的局部变量,但可以通过lambda的捕获列表来实现
//捕获列表的作用是帮助lambda捕获除全局变量以外,其他外部变量的
void work()
{
//int b;
//auto a = [] {b; return 0; };//b报错 lambda的本质是函数,不能在函数内部访问另一个函数的局部变量
//通过lambda的捕获列表来实现对另一个函数局部变量的访问
int c,e;
auto t = [c,&e]
{
//c=3;//报错:c是值捕获,只可读不可以修改
e = 8;//不报错: e是引用捕获,可读可修改
cout << e << endl;
return 0;
};
t();
}
int main()
{
work();
return 0;
}
捕获列表的相关判断:
int main()
{
int a = 10, b = 20;
auto f1 = [] {return a; }; //错误,没有捕获外部变量,因此无法访问变量 a
auto f2 = [&] {return a++; }; //正确,使用引用的方式捕获外部变量,可读写
auto f3 = [=] {return a; }; //正确,使用值拷贝的方式捕获外部变量,可读
auto f4 = [=] {return a++; };//错误,使用值拷贝的方式捕获外部变量,可读不能写
auto f5 = [a] {return a + b; }//错误,使用拷贝的方式捕获了外部变量 a,没有捕获外部变量 b,因此无法访问变量 b
auto f6 = [a, &b] {return a + (b++); };//正确,使用值拷贝的方式捕获了外部变量 a,只读,使用引用的方式捕获外部变量 b,可读写
auto f7 = [=, &b] {return a + (b++); };//正确,使用值拷贝的方式捕获所有外部变量以及 b 的引用,b 可读写,其他只读
return 0;
}
//this指针:this指针在类内成员函数的隐含参数里,当对象调用成员函数时,会将对象的地址传给this指针
//类内的成员函数只有一份代码放在代码区,所有对象调用的是同一个成员函数
//如果在成员函数内修改对象的成员变量是通过this指针来区分修改的是哪个对象的成员变量
//在类内,如果lambda在成员函数内想捕获成员变量,只需捕获this即可
class Test
{
public:
void output(int x, int y)
{
auto x1 = [] {return m_number; }; //错误,没有捕获外部变量,不能使用类成员 m_number
auto x1 = [ m_number] { return m_number; };//错误,不知道this指向谁,捕获的m_number是哪个对象的
auto x2 = [=] {return m_number + x + y; };//正确,以值拷贝的方式捕获所有外部变量包括this,因为this是output的隐含参数
auto x3 = [&] {return m_number + x + y; };//正确,以引用的方式捕获所有外部变量
//通过捕获this指针,用this-> m_number去访问m_number
auto x4 = [this] {return m_number; };//正确,捕获 this 指针,可访问对象内部成员
auto x5 = [this] {return m_number + x + y; };//错误,捕获 this 指针,可访问类内部成员,没有捕获到变量 x,y,因此不能访问。
auto x6 = [this, x, y] {return m_number + x + y; };// 正确,捕获 this 指针,x,y
auto x7 = [this] {return m_number++; }; //正确,捕获 this 指针,并且可以修改对象内部变量的值
}
int m_number = 100;
};
返回值
一般情况下,不指定lambda表达式的返回值,编译器会根据return语句自动推导返回值类型,但是需要注意的是lambda表达式不能通过列表初始化自动推导出返回值类型,因为不知道列表是哪种类型(有vector,list等等),需要正确显示声明了函数的返回值类型是哪种类型的。
// 可以自动推导出返回值类型
auto f = [](int i)
{
return i;
}
// 不能推导出返回值类型
auto f1 = []()
{
return { 1, 2 }; // 基于列表初始化推导返回值,错误
}
// 正确显示声明了函数的返回值类型是数组类型的
auto f1 = []()-> vector<int>
{
return { 1, 2 }; // 正确
};
vector<int> a = {1,5,6};
函数指针
//函数比较器
//返回值为bool类型
#include <algorithm>
bool cmp(int a, int b)
{
//return a > b;//从大到小排序
return a < b;//从小到大排序
}
int main()
{
vector<int> c = {0,-1,4,-2,-9,4,2,8};
sort(c.begin(),c.end(),cmp);
for (auto it : c)
{
cout << it << " ";
}
return 0;
}
class A {
public:
string Name;
int Age;
A(string name, int age) :Name(name), Age(age) {}
};
bool cmp(const A &a,const A &b)//使用引用避免拷贝构造,const万能引用,既能接收左值又能接收右值
{
return a.Age < b.Age;
}
int main()
{
vector<A> c = {A("张三",12),A("李四",7),A("王五",19)};
//sort(c.begin(), c.end(), cmp);
sort(c.begin(), c.end(), [](const A& a, const A& b)
{
return a.Age < b.Age; //Age从小到大排序
});
for (auto it: c)
{
cout << it.Name << " " << it.Age << endl;
}
return 0;
}