目录
前言-lambda表达式的介绍:
1、lambda表达式的作用
2、lambda表达式的优势
2.1 用lambda构建lambda变量
3、lambda表达式的类型
4、捕捉列表说明
4.1 传值捕捉
4.2 mutable
4.3 传引用捕捉
4.4 混合捕捉
5、lambda的大小
结语
前言-lambda表达式的介绍:
C++11推出lambda表达式,lambda表达式实际上是一个匿名函数,他的具体书写格式如下:
lambda格式:[capture-list] (parameters) mutable -> return-type { statement }
1、[capture-list]表示捕捉列表,他是lambda函数的标志性符号,编译器会根据[]来判断该式子是否为lambda函数,他的另一个作用是可以捕捉父类作用域的变量给到lambda函数使用,因此叫捕捉列表。
2、(parameters)表示lambda的参数列表,他的用法和意义跟普通函数的形参列表是一样的,如果不需要对lambda传任何参数则可以连同括号一起省略。
3、mutable的作用是消除捕捉列表中的常属性,因为lambda的捕捉列表中的变量都是默认被const修饰的,因此lambda函数内部是不能修改[capture-list]中的参数,但是加上mutable后就可以在函数体内部修改捕捉的参数了。注意:如果加上mutable则参数列表(parameters)必须有参数,即使参数列表为空。
4、-> return-type表示lambda函数的返回值类型,他的意义等同于普通函数的返回类型,只不过在lambda函数中可以省略-> return-type(不管lambda函数有没有返回值都可以省略),由编译器自动推导该lambda函数的返回值类型。
5、{ statement }表示lambda的函数体,因为lambda实际上是一个匿名函数,所以他也有属于自己的函数体,并且在捕捉列表中的变量可以在该函数体内使用。
从以上叙述可以得到一个有趣的写法,因为lambda表达式中只有捕捉列表和函数体的标识符不能省略,因此最简单的lambda函数可以写为:[]{},该lambda函数没有实现任何的功能。
1、lambda表达式的作用
为什么会C++11会推出lambda表达式呢?因为在此之前,我们如果要自己实现一个仿函数则需要手写一个类,如果要实现的逻辑复杂而多样,则要实现多个类进行仿函数的调用,类一旦多了起来就会有重名的烦恼,并且实现的类的目的仅仅是为了的仿函数调用,难免会感到大材小用,比如以下示例:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct Shop
{
string _name;//商品名字
double _price;//价格
int _evaluate; // 评价
Shop(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
friend ostream& operator<<(ostream& out, const Shop& s);
};
ostream& operator<<(ostream& out, const Shop& s)//流提取
{
cout << s._name << " " << s._price << " " << s._evaluate << endl;
return out;
}
struct ComparePriceLess//价格低的优先
{
bool operator()(const Shop& gl, const Shop& gr)
{
return gl._price < gr._price;
}
};
struct ComparePriceGreater//价格高的优先
{
bool operator()(const Shop& gl, const Shop& gr)
{
return gl._price > gr._price;
}
};
int main()
{
vector<Shop> v = { { "苹果", 12.2, 5 }, { "香蕉", 3.3, 4 }, { "橙子", 6.6,
3 }, { "菠萝", 1.5, 4 } };
sort(v.begin(), v.end(), ComparePriceLess());
//打印
for (auto num : v)
{
cout << num;
}
cout << endl;
sort(v.begin(), v.end(), ComparePriceGreater());
//打印
for (auto num : v)
{
cout << num;
}
cout << endl;
}
运行结果:
从结果可以看到,虽然创建类调用仿函数可以有效的实现我们的要求,但是排序的逻辑不同就要再多写一个类来调用仿函数, 人们觉得该写法过于复杂,因此推出lambda表达式来代替以上的写法。
2、lambda表达式的优势
比如以上代码,若用lambda表达式代替仿函数的类,优化后代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct Shop
{
string _name;//商品名字
double _price;//价格
int _evaluate; // 评价
Shop(const char* str, double price, int evaluate)
:_name(str)
, _price(price)
, _evaluate(evaluate)
{}
friend ostream& operator<<(ostream& out, const Shop& s);
};
ostream& operator<<(ostream& out, const Shop& s)//流提取
{
cout << s._name << " " << s._price << " " << s._evaluate << endl;
return out;
}
int main()
{
vector<Shop> v = { { "苹果", 12.2, 5 }, { "香蕉", 3.3, 4 }, { "橙子", 6.6,
3 }, { "菠萝", 1.5, 4 } };
//sort(v.begin(), v.end(), ComparePriceLess());
sort(v.begin(), v.end(),
[](const Shop& gl,const Shop& gr)->bool {return gl._price < gr._price; });
//打印
for (auto num : v)
{
cout << num;
}
cout << endl;
//sort(v.begin(), v.end(), ComparePriceGreater());
sort(v.begin(), v.end(),
[](const Shop& gl, const Shop& gr)->bool {return gl._price > gr._price; });
//打印
for (auto num : v)
{
cout << num;
}
cout << endl;
}
运行结果:
从结果可以发现,用lambda表达式可以完美的替代类调用仿函数,并且在代码量上也得到了显著的减少。
2.1 用lambda构建lambda变量
以上虽然是lambda函数的用法之一,但是lambda函数习惯先给到一个变量,然后再用该变量去调用lambda函数,因为这样写可以提高代码的可读性。
示例如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
//构建lambda对象add
//auto add = [](int x, int y)->int {return x + y; };
auto add = [](int x, int y) {return x + y; };//省略返回值的写法
int sum = add(10, 20);//用lambda对象add去调用lambda函数
cout << sum << endl;
//若直接用lambda表达式调用则可读性不高
cout << [](int x, int y) {return x + y; }(13, 13) << endl;
return 0;
}
运行结果:
3、lambda表达式的类型
lambda构建的变量虽然看起来都是lambda类型,但是他们相互之间不能够直接赋值,因为在底层,每个lambda构建的变量的类型都存在细微的差异。
示例代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
auto st1 = [] {cout << "hello world1" << endl; };
auto st2 = [] {cout << "hello world2" << endl;};
//st1 = st2;//lambda对象之间不能够相互赋值
auto st3(st1);//但是可以拷贝构造一个新的lambda变量
st3();
//lambda可以赋值返回值类型相同的函数指针
void(*pf)();
pf = st2;
pf();
return 0;
}
运行结果:
4、捕捉列表说明
如下图所示,虽然lambda表达式在main函数中,但是lambda表达式的函数体中不能直接用main函数的变量:
4.1 传值捕捉
捕捉列表可以捕捉父类作用域里的变量(也就是上述的main作用域的变量),并且可以在lambda表达式中用到捕捉的变量,写法如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
auto swap = [x, y]//此处省去了括号()
{
cout << x << " " << y << endl;
};
swap();
return 0;
}
运行结果:
默认写法的捕捉方式均为传值捕捉,传值捕捉的写法还可以用一个‘=’号来表示,即以传值方式捕捉外部父作用域的所有变量,代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
auto swap = [=]//'='号表示以传值方式捕捉外部所有变量
{
cout << x << " " << y << endl;
};
swap();
return 0;
}
以上的传值捕捉,即对x和y的改变不会影响main函数中的x和y,但是我们发现此时不能直接对捕捉的x和y进行修改:
因为lambda的捕捉列表中的变量都是默认被const修饰的,因此lambda函数内部是不能修改[capture-list]中的参数,这时候要用关键字mutable才可以对捕捉数据进行修改。
4.2 mutable
mutable的作用是消除捕捉列表中的常属性,具体写法如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
auto swap = [=]()mutable//mutable
{
x++;
y++;
cout << x << " " << y << endl;
};
swap();
cout << x << " " << y << endl;
return 0;
}
运行结果:
而且从上面代码中可以发现,之前都省略了括号‘()’,但是如果用到了mutable关键字后,即使参数列表中没有参数也不能省略括号‘()’。
并且从运行结果可以发现,传值捕捉是无法更改外部x和y的值,若想更改外部的x和y需用传引用捕捉。
4.3 传引用捕捉
传引用捕捉的符号为:‘&’,他也有两层用法,一是直接在捕捉列表中写&,表示以传引用的形式捕捉父作用域所有变量,二是对要捕捉的变量前面加上&。
具体示例代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
cout << x << " " << y << endl;
//auto swap = [&x, &y]
auto swap = [&]
{
int temp = x;
x = y;
y = temp;
};
swap();
cout << x << " " << y << endl;
return 0;
}
运行结果:
从结果可以发现,lambda内部对x和y进行修改会影响外部的x和y(并且传引用捕捉不需要mutable也能改变捕捉变量的值)。
捕捉列表总结:
1、[var]:表示传值方式捕捉变量 var2、[=]:表示传值方式捕获所有父作用域中的变量( 包括 this指针)3、[&var]:表示传引用方式捕捉变量 var4、[&]:表示传引用方式捕捉所有父作用域中的变量( 包括 this指针)
4.4 混合捕捉
传值捕捉和传引用捕捉两种写法可以相互交错使用,把这样的使用方式叫做混合捕捉,比如以下代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
cout << x << " " << y << endl;
auto swap = [&,y]()mutable//除了y是传值捕捉,其他都是传引用捕捉
{
x++;
y++;
};
swap();
cout << x << " " << y << endl;
return 0;
}
运行结果:
可以从结果看到,x是传引用捕捉,因此外面x的值发生了变化,而y是传值捕捉,则修改内部的x不会影响外部的y。
5、lambda的大小
因为底层还是会把lambda表达式当成一个仿函数的类去处理,所以lambda表达式中的参数列表会作为类中operator=()运算符重载的参数列表,lambda的函数体对应operator=()的函数体,而捕捉列表就对应的是类的成员。
当捕捉列表什么都没有捕捉的时候,lambda的大小为1(表示对象的占位),示例代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
auto swap = [](int x,int y)mutable
{
int temp = x;
};
cout << sizeof(swap) << endl;
return 0;
}
运行结果:
当lambda的捕捉列表中有捕捉的数据,则对标仿函数的类中含有成员变量的情况, 代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{
int x = 1, y = 2;
auto swap = [y](int x,int y)mutable//捕捉了y
{
int temp = x;
};
cout << sizeof(swap) << endl;
return 0;
}
运行结果:
结语
以上就是关于lambda表达式的讲解,lambda实际上是为了避免程序员写仿函数时需要写大量的类而提出的一个新语法,本来让程序员实现的仿函数的类交给由编译器去完成,减轻了程序员的工作。
最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!