学习形如钻子,必须集中在一点钻下去才有力量。
大家好,这篇文章和大家分享一下Lambda函数,以及对它的理解,看完这一篇文章,你会对Lambda会有一个基本的认识。
第一到第四都是lambda函数的含义以及用法,第五是lambda函数的本质,实际上就是一个重载函数,很巧妙。
Lambda函数是C++11标准新增的语法糖,也称为lambda表达式或者匿名函数。
Lambda函数的特点是:简洁,距离近,高校和功能强大。
语法:
示例:
[](int a)->int{cout<<"lambda表达式很好用"<<endl;}
捕获列表是lambda的核心,整个lambda也就这个比较复杂,要记得东西比较多,其他部分比较简单,所以把捕获列表这个重点放在最后解释,请听我一一道来。
此时你可能看不懂,但是没关系啦。,后面一一展开说明各个部分所起的作用和所表达的意思。
一.参数列表
参数列表是随机的,类似普通函数的参数列表,如果没有参数列表,()可以省略不写。
lambda函数与普通函数参数列表的不同:
(1)lambda函数不能有默认参数。
(2)所有参数必须有参数名。
(3)不支持可变参数。
二、返回类型
用后置的方法书写返回类型,类似于普通函数的返回类型,如果不写返回类型,编译器会根据函数体中的代码推断出来。如果有返回类型,建议显式的指定,自动推断可能与预期不一致。
三、函数体
类似于普通函数的函数体。
四、捕获列表
前言&引入:
通过捕获列表,lambda函数可以访问父作用域函数体定义的非静态变量)中的非静态局部变量(静态局部变量可以直接访问,不能访问全局变量)。
此话抽象,不妨直接上代码解释上面这一句话:
#include<iostream> using namespace std; int main() { int ii = 10; auto f = [ii](int a) {cout << a + ii; }; f(1);//f(1)里面的1对应(int a),把1传给参数a,然后执行函数体里面的内容打印出11 return 0; }
注意:因为这个变量ii在lambda函数的捕获列表中,如果ii不在的话,编译器就会报错。
代码验证及其报错如下:
捕获列表书写在[]中,与函数参数的传递类似,捕获方式可以是值和引用。以下列出了不同的捕获列表的方式。
1.值捕获
·与传递参数类似,采用值捕获(还记得交换两个整形变量使用的方法有值传递和址传递的方法吗?这里可·以仿造这个知识点进行理解)的前提是变量可以拷贝。
·与传递参数不同,变量的值是在lambda函数创建时拷贝,而不是调用时拷贝。
例如:
size_t v1 = 42; auto f = [ v1 ] { return v1; }; // 使用了值捕获,将v1拷贝到名为f的可调用对象。 v1 = 0; auto j = f(); // j为42,f保存了我们创建它是v1的拷贝。
由于被捕获的值是在lambda函数创建时拷贝,因此在随后对其修改不会影响到lambda内部的值。
默认情况下,如果以传值方式捕获变量,则在lambda函数中不能修改变量的值。
2. 引用捕获
·和函数引用参数一样,引用变量的值在lambda函数体中改变时,将影响被引用的对象。
size_t v1 = 42; auto f = [ &v1 ] { return v1; }; // 引用捕获,将v1拷贝到名为f的可调用对象。 v1 = 0; auto j = f(); // j为0。
注意:如果采用引用方式捕获变量,就必须保证被引用的对象在lambda执行的时候是存在的。
3. 隐式捕获
· 除了显式列出我们希望使用的父作域的变量之外,还可以让编译器根据函数体中的代码来推断需要捕获哪些变量,这种方式称之为隐式捕获。
· 隐式捕获有两种方式,分别是[=]和[&]。[=]表示以值捕获的方式捕获外部变量,[&]表示以引用捕获的方式捕获外部变量。
int a = 123; auto f = [ = ] { cout << a << endl; }; //值捕获 f(); // 输出:123 auto f1 = [ & ] { cout << a++ << endl; }; //引用捕获 f1(); //输出:123(采用了后++) cout << a << endl; //输出 124
4.混合方式捕获
·lambda函数还支持混合方式捕获,即同时使用显式捕获和隐式捕获。
·混合捕获时,捕获列表中的第一个元素必须是 = 或 &,此符号指定了默认捕获的方式是值捕获或引用捕获。
注意:显式捕获的变量必须使用和默认捕获不同的方式捕获。
例如:
int i = 10; int j = 20; auto f1 = [ =, &i] () { return j + i; }; // 正确,默认值捕获,显式是引用捕获 auto f2 = [ =, i] () { return i + j; }; // 编译出错,默认值捕获,显式值捕获,冲突 auto f3 = [ &, &i] () { return i +j; }; // 编译出错,默认引用捕获,显式引用捕获, 冲突了
5.修改值捕获变量的值
·在lambda函数中,如果以传值方式捕获变量,则函数体中不能修改该变量,否则会引发编译错误。
·在lambda函数中,如果希望修改值捕获变量的值,可以加mutable选项,但是,在lambda函数的外部,变量的值不会被修改。
直接上代码:
int a = 123; auto f = [a]()mutable { cout << ++a << endl; }; // 不会报错 cout << a << endl; // 输出:123 f(); // 输出:124 cout << a << endl; // 输出:123
五.Lambda函数的本质
当我们编写了一个lambda函数之后,编译器将它翻译成一个类,该类中有一个重载了()的函数。
1.采用值捕获
采用值捕获时,lambda函数生成的类用捕获变量的值初始化自己的成员变量。
例如:
int a =10; int b = 20; auto addfun = [=] (const int c ) -> int { return a+c; }; int c = addfun(b); cout << c << endl; 等同于: class Myclass { int m_a; // 该成员变量对应通过值捕获的变量。 public: Myclass( int a ) : m_a(a){}; // 该形参对应捕获的变量。 // 重载了()运算符的函数,返回类型、形参和函数体都与lambda函数一致。 int operator()(const int c) const { return a + c; } };
默认情况下,由lambda函数生成的类是const成员函数,所以变量的值不能修改。如果加上mutable,相当于去掉const。这样上面的限制就能讲通了。
2.采用引用捕获
注意:如果lambda函数采用引用捕获的方式,编译器直接引用就行了。
唯一需要注意的是,lambda函数执行时,程序必须保证引用的对象有效。
看到这里希望能助你一臂之力,让你对Lambda有更好的理解。
2023.02.07
From:努力进大厂的新青年