lambda表达式
含义:
lambda表达式其实就是函数的用法,且以对象的方式匿名去用。
语法:
【capture-list】(parameters)mutable->return-type{statement}
- caputure-list : 捕捉列表。捕捉上下文中的变量供lambda函数使用
- parameters:参数列表。 与普通函数的参数列表一致。可省略
- mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常性
- ->returntype:返回值类型。用于追踪返回类型形式声明函数的返回值类型。
- 没有返回值可省略
- 有返回值,也可以省略。由编译器推导
- {statemet}:函数体。 可以使用捕捉列表中的变量
注意:在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{};该lambda函数不能做任何事情。
捕捉列表说明:
- 【val】:表示以值传递的方式捕捉变量var
- 【=】:表示以传递的方式捕捉后面{ }中的所有变量
- 【&val】: 表示引用传递捕捉var变量
- 【&】:表示引用传递捕捉所有 { }的变量
注意:
-
若语法上捕捉列表由多个捕捉项组成,并以逗号分隔。
比如:【=,&a】:以引用传递的方式捕捉变量a,并以值传递的方式捕捉其他变量 【&,a】:值传递的方式捕捉变量a,因引用方式捕捉其它变量 【=,a】:已经以值传递的方式捕捉了所有的变量,捕捉a会重复
例1:
int main()
{
//最简单的lambda表达式,没有任何意义
[]{};
int a = 3;
int b = 4;
auto e = [a,b] {return a + b ;}; //省略参数类型和返回值类型 ;【a,b】捕捉a,b
cout << e() <<endl; //没有参数,所以要()
auto f = [=] {return a + b;}; //代表捕捉外边的所有对象
cout << f() << endl;
system("pause");
}
结果:
总结:其实lambda表达式可以理解为无名函数,直接被auto接收。 后面有讲怎么匿名调用
例2:
int main()
{
int a = 3;
int b = 4;
// auto func2 = [=](int c)->int{return b += a + c; };
//错误,不能在非可变的lambda中修改值。 【=】只是以传值的方式捕获,不能修改
//所以我们要把它改为可变的。
//方法1:加mutable
auto func1 = [=](int c)mutable->int{return b += a+c;};
//方法2: 在捕获列表中加b的引用
auto func2 = [=,&b](int c)->int{return b += a + c; };
cout << func1(10) << endl;
cout << func2(10) << endl;
system("pause");
}
结果:
例3:交换a、b的lambda的例子:
int main()
{
int a = 3;
int b = 4;
cout << "刚开始:" << endl;
cout << "a:" << a << " " << "b:" << b << endl;
//mutable改变捕捉到的a、b拷贝可以改变
auto lamber_swap1 = [a, b]()mutable->void{int tmp = a; a = b; b = tmp; };
lamber_swap1(); //不能发生交换,传值拷贝的方式捕捉a,b
//正确的写法
//捕捉列表[]中捕捉a,b的引用
auto lamber_swap2 = [&a, &b](){int tmp = a; a = b; b = tmp; };
lamber_swap2();
cout << "a:" << a << " " << "b:" << b << endl;
//捕捉列表中不写,但是参数列表中包含a,b的引用
auto lamber_swap3 = [](int &a, int& b) {int tmp = a; a = b; b = tmp; };
lamber_swap3(a,b);
cout << "a:" << a << " " << "b:" << b << endl;
//捕捉[&]列表
auto lamber_swap4 = [&](){int tmp = a; a = b; b = tmp; };
lamber_swap4();
cout << "a:" << a << " " << "b:" << b << endl;
system("pause");
}
结果:
我们接下来可以通过类比函数指针、仿函数,体验lambda函数表达式的亮点和方便。
函数指针、仿函数与lambda表达式 之间的类比:
- 函数指针
- 仿函数
- lambda 表达式
为什么说lambda表达式能类比于函数和仿函数呢?
template<class ..., class Compare>
void sort(first, last, Compare comp);
以上三个都可以传到comp这里,因为这里是一个模板,既可以传函数指针,也可以传仿函数,传lambda表达式
struct Stu
{
string _no;
int Eng;
int math;
};
bool SortBymath(const Stu& s1, const Stu& s2)
{
return s1.math < s2.math;
}
struct SortBymath_
{
bool operator()(const Stu& s1, const Stu& s2)
{
return s1.math < s2.math;
}
};
int main()
{
Stu s[] = { { "101", 98, 99 }, { "102", 64, 79 }, { "100", 99, 74 } };
//按数学成绩排序的三种写法
//1.函数指针
//按数学成绩整体排升序
sort(s, s + sizeof(s) / sizeof(s[0]), SortBymath);
//2.仿函数排序
//方法1:定义一个仿函数对象传对象
SortBymath_ smath;
sort(s, s + sizeof(s) / sizeof(s[0]), smath);
//方法2:本身直接传递
sort(s, s + sizeof(s) / sizeof(s[0]), SortBymath_());
//3.lambda表达式
//math降序
auto mathLess = [](const Stu& s1, const Stu& s2)
{
return s1.math < s2.math;
};
//math升序
auto mathGreater = [](const Stu& s1, const Stu& s2)
{
return s1.math > s2.math;
};
sort(s,s+sizeof(s)/sizeof(s[0]),mathLess); //排降序
sort(s,s+sizeof(s)/sizeof(s[0]),mathGreater); //排升序
system("pause");
}
注意:lambda表达式其实都不用起名字,直接用就ok了。就上面的例子
int main()
{
//按数学成绩升序排序
sort(s,s+sizeof(s)/sizeof(s[0]), [](const Stu& s1, const Stu& s2)
{
return s1.math < s2.math;
};
//按数学成绩降序排序
sort(s,s+sizeof(s)/sizeof(s[0]), [](const Stu& s1, const Stu& s2)
{
return s1.math > s2.math;
};
//也可以按英语成绩,类似。
}
函数指针 ,仿函数,C++11lambda表达式三者的对比:
- 函数指针
C语言沿袭而来,当做函数参数传进sort()中进行按自定义类型的排序 - 仿函数
优:相比函数指针更加方便。也可以作为模板参数传参,也可以定义成对象传参。
缺:若比较的参数过多,定义过多的模板结构体 - lambda表达式
若结构体中的变量很多, 需要比较的参数变量也很多。那么我么就会写很多个函数或者仿函数,这样就很麻烦。 而lambda表达式的诞生就是为了解决这类问题。可以直接在调用函数的地方写匿名的lambda表达式。
而且使用简便,代码简洁,可读性强。