C C++最新c++中lambda表达式用法_cpp lambda(1),2024年最新2024C C++进阶学习资料

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

{
auto print = [](int s) {std::cout << "value is " << s << std::endl;};
auto lambAdd = [](int a, int b) ->int { return a + b;};
int iSum = lambAdd(10, 11);
print(iSum);

return 0;

}


lambAdd有两个入参a和b,然后它的返回类型是int,我们可以试一下把`->int`去掉,结果是一样的。


###### 2 lambda捕获块


###### 2.1 捕获的简单使用


在第1节中,我们展示了lambda的语法形式,后面的形参和函数体之类都好理解,那么方括号里面捕获是啥意思呢?


其实这里涉及到lambda表达式一个重要的概念,就是闭包。


这里我们需要先对lambda表达式的实现原理做一下说明:当我们定义一个lambda表达式后,编译器会自动生成一个匿名类,这个类里面会默认实现一个public类型的operator()函数,我们称为闭包类型。那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,它是一个右值。


所以,我们上面的lambda表达式的结果就是一个一个的闭包。闭包的一个强大之处是可以通过传值或者引用的方式捕获其封装作用域内的变量,前面的方括号就是用来定义捕获模式以及变量,所以我们把方括号[]括起来的部分称为捕获块。


看这个例子:



#include

int main()
{
int x = 10;
auto print = [](int s) {std::cout << "value is " << s << std::endl;};
auto lambAdd = [x](int a) { return a + x;};
auto lambAdd2 = [&x](int a, int b) { return a + b + x;};
auto iSum = lambAdd(10);
auto iSum2 = lambAdd2(10, 11);
print(iSum);
print(iSum2);

return 0;

}


当lambda块为空时,表示没有捕获任何变量,不为空时,比如上面的lambAdd是以复制的形式捕获变量x,而lambAdd2是以引用的方式捕获x。那么这个复制或者引用到底是怎么体现的呢,我们使用gdb看一下lambAdd和lambAdd2的具体类型,如下:



(gdb) ptype lambAdd
type = struct <lambda(int)> {
int __x;
}
(gdb) ptype lambAdd2
type = struct <lambda(int, int)> {
int &__x;
}
(gdb)


前面我们说过lambda实际上是一个类,这里得到了证明,在c++中struct和class除了有少许区别,其他都是一样的,所以我们可以看到复制形式捕获实际上是一个包含int类型成员变量的struct,引用形式捕获实际上是一个包含int&类型成员变量的struct,然后在运行的时候,会使用我们捕获的数据来初始化成员变量。


既然有初始化,那么必然有构造函数啊,然后捕获生成的成员变量,有operator()函数,暂时来讲,一个比较立体的闭包类型就存在于我们脑海中啦,对于lambda表达式类型具体组成,我们暂时放一放,接着说捕获。


###### 2.2 捕获的类型


捕获的方式可以是引用也可以是复制,但是到底有哪些类型的捕获呢?


捕获类型如下:


* []:默认不捕获任何变量;
* [=]:默认以复制捕获所有变量;
* [&]:默认以引用捕获所有变量;
* [x]:仅以复制捕获x,其它变量不捕获;
* [x…]:以包展开方式复制捕获参数包变量;
* [&x]:仅以引用捕获x,其它变量不捕获;
* [&x…]:以包展开方式引用捕获参数包变量;
* [=, &x]:默认以复制捕获所有变量,但是x是例外,通过引用捕获;
* [&, x]:默认以引用捕获所有变量,但是x是例外,通过复制捕获;
* [this]:通过引用捕获当前对象(其实是复制指针);
* [\*this]:通过复制方式捕获当前对象;


可以看到,lambda是可以有多个捕获的,每个捕获之间以逗号分隔,另外呢,不管多少种捕获类型,万变不离其宗,要么以复制方式捕获,要么以引用方式捕获。


那么复制捕获和引用捕获到底有什么区别呢?


标准c++规定,默认情况下,在lambda表达式中,对于operator()的重载是const属性的,也就意味着如果以复制形式捕获的变量,是不允许修改的,看这段代码:



#include

int main()
{
int x = 10;
int y = 20;
auto print = [](int s) {std::cout << "value is " << s << std::endl;};
auto lambAdd = [x](int a) {
// x++; 此处x是只读,不允许自增,编译会报错
return a + x;
};
auto lambAdd2 = [&x](int a, int b) {
x = x+5;
return a + b + x;
};
auto iSum = lambAdd(10);
auto iSum2 = lambAdd2(10, 11);
print(iSum);
print(iSum2);

return 0;

}


从代码可以看出,复制捕获不允许修改变量值,而引用捕获则允许修改变量值,为什么呢,这里我理解,&x实际上是一个int\*类型的指针,所以我们可以修改x的值,因为我们只是对这个指针所指向的内容进行修改,并没有对指针本身进行修改,且与我们常规声明的引用类型入参一样,修改的值在lambda表达式外也是有效的。


那么如果我想使用复制捕获,又想修改变量的值呢,这时我们就想起来有个关键字,叫做mutable,它允许在常成员函数中修改成员变量的值,所以我们可以给lambda表达式指定mutable关键字,如下:



#include

int main()
{
int x = 10;
int y = 20;
auto print = [](int s) {std::cout << "value is " << s << std::endl;};
auto lambAdd = [x](int a) mutable {
x++;
return a + x;
};
auto iSum = lambAdd(10);
print(iSum);
print(x);

return 0;

}


执行结果如下:



value is 21
value is 10


所以加上mutable以后就可以对复制捕获进行修改,但有一点,它的修改出了lambda表达式以后就无效了。


###### 2.3 包展开方式捕获


仔细看2.2节中捕获类型,会发现有[x…]这样的类型,它实际上是以复制方式捕获了一个可变参数,在c++中其实涉及到了模板形参包,也就是变参模板,看下面例子:



#include

void tprintf()
{
return;
}

template<typename U, typename …Ts>
void tprintf(U u, Ts… ts)
{
auto t = [ts…]{
tprintf(ts…);
};
std::cout << "value is " << u << std::endl;
t();
return;
}

int main()
{
tprintf(1,‘c’,3, 8);
return 0;
}


它捕获了一组可变的参数,不过这里实际上是为了演示对可变参数的捕获,强行使用了lambda表达式,不使用的话,代码可能更加简洁,我们只需要通过这个演示知道怎么使用即可,另外对于变参模板的使用,这里就不展开来讲了。


###### 2.4 捕获的作用


我再看lambda的捕获的时候一直很奇怪,初看的话,这个捕获跟传参数有什么区别呢,都是把一个变量值传入lambda表达式体供使用,但仔细思考的话,它是有作用的,假设有这么一个案例,一个公司有999名员工,每个员工的工号是从1~999,我们现在想找出工号是8的整数倍的所有员工,一个可行的代码如下:



#include
#include

int main()
{
int x = 8;
auto t = [x](int i){
if ( i % x == 0 )
{
std::cout << "value is " << i << std::endl;
}
};
auto t2 = [](int i, int x){
if ( i % x == 0 )
{
std::cout << "value is " << i << std::endl;

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

)]
[外链图片转存中…(img-jibdguU1-1715713284787)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值