所谓lambda
表达式,是一种为了更方便实现回调和简单逻辑的函数写法,应用于C++ 11的新特性中。
Lambda表达式是一种匿名函数,它可以作为参数传递给其他函数或方法。它通常用于函数式编程,可以简化代码并提高代码的可读性。
lambda
具体的构成分为 不可隐藏部分和可隐藏部分。全量写法是:[capture list](parameter list)->return type function body
速记为:[]()->return {}
详细介绍上面的写法是:
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体。如下
auto f = []{return 42;);
这里使用了 auto
关键字,这是类型推断关键字。用来让编译器自行推断变量类型,简化代码风格。
其次就是简化后的lambda表达式。 这里我们简化了形参和返回类型显式声明。只留下局部变量捕获列表,和函数体。
如上代码所述,局部变量捕获列表,和函数体。是lambda 表达式中不可省略的部分,这里要说明:[]
是局部变量捕获列表,全局变量不在这里捕获。详细其他介绍请看下面的分结构解析部分。
{}
是函数体部分,这里要说明的是,编译器会通过类型推定决定函数返回类型是什么,如果有除return 外的其他内容,将会认为应当返回void
类型。 详细其他介绍请看下面的分结构解析部分。
Lambda表达式和函数的区别在于,Lambda表达式是一种匿名函数,可以在代码中直接定义和使用,不需要像传统函数一样需要先定义再调用。Lambda表达式通常用于函数式编程,可以简化代码并提高代码的可读性。Lambda表达式可以作为参数传递给其他函数或方法,也可以在函数内部定义和使用。
而函数是一种有名字的代码块,需要先定义再调用。函数通常用于封装一段可重用的代码,可以接受参数并返回值。函数可以在程序的任何地方定义和调用,也可以作为参数传递给其他函数或方法。函数在C++中是一种重要的编程概念,可以帮助我们组织代码并提高代码的可维护性。
让我们换一种通俗的说法吧:
对于那种只在一两个地方使用的简单操作,lambda表达式是最有用的。如果我们需要在很多地方使用相同的操作,通常应该定义一个函数,而不是多次编写相同的lambda表达式。类似的,如果一个操作需要很多语句才能完成,通常使用函数更好。
auto f = []{return 42;);
cout << f() << endl; // 打印结果是42
lambda表达式的使用方式是和函数一样的,当然,使用注意细节还是不一致的,比如,lambda表达式是不能在构造、析构函数中使用的,这就让我们最好不要用lambda表达式实现类似于 info()
、close()
之类的初始化和清理函数了。
[]
这里被称之为捕获,而接下来,我们将要说明一下捕获的种类。
同比与值拷贝,是将原值拷贝到新的内存空间中,修改原值时,不会修改拷贝值。
使用方法:[name] {}
这里的name 就是需要捕获的局部变量。
类似参数传递,变量的捕获方式也可以是值或引用。下面表中列出了几种不同的构造捕获列表的方式。
到目前为止,我们的lambda采用值捕获的方式。与传值参数类似,采用值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值是在 lambda 创建时拷贝,而不是调用时拷贝:
void fcn1()
{
size_t v1=42;//局部变量
//将v1拷贝到名为f的可调用对象
auto f = [v1]{return vl;};
v1=0;
auto j=f();//j为42;f保存了我们创建它时v1的拷贝
}
由于被捕获变量的值是在lambda创建时拷贝,因此随后对其修改不会影响到lambda内对应的值。
我们定义lambda时可以采用引用方式捕获变量。例如:
void fcn2()
{
size t v1=42;//局部变量
//对象f2包含v1的引用
auto f2 = [&v1]{return vl;}
v1=0;
auto j=f2();//j为0;f2保存v1的引用,而非拷贝
}
v1之前的&
指出v1应该以引用方式捕获。一个以引用方式捕获的变量与其他任何类型的引用的行为类似。当我们在lambda函数体内使用此变量时,实际上使用的是引用所绑定的对象。
在本例中,当lambda返回v1时,它返回的是vl指向的对象的值。
引用捕获与返回引用 有着相同的问题和限制。
lambda捕获的都是局部变量,这些变量在函数结束后就不复存在了。如果lambda可能在函数结束后执行,捕获的引用指向的局部变量已经消失。
引用捕获有时是必要的。
例如,我们可能希望biggies函数接受一个ostream的引用,用来输出数据,并接受一个字符作为分隔符:
void biggies(vector &words,
vector:size_type sz,
ostream &os = cout, char c = ' ')
{
//与之前例子一样的重排words的代码
//打印count的语句改为打印到os
for_each (words.begin(), words.end(),
[&os, c](const string &s) {os <
我们不能拷贝ostream对象 ,因此捕获os的唯一方法就是捕获其引用(或指向os的指针)。
当我们向一个函数传递一个lambda时,就像本例中调用for each那样,lambda
会立即执行。在此情况下,以引用方式捕获os没有问题,因为当for_each执行时,
biggies中的变量是存在的。
我们也可以从一个函数返回lambda。函数可以直接返回一个可调用对象,或者返回一
个类对象,该类含有可调用对象的数据成员。如果函数返回一个lambda,.则与函数不能返回一个局部变量的引用类似,此lambda也不能包含引用捕获。
除了显式列出我们希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体中的代码来推断我们要使用哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&
或=
。&
告诉编译器采用捕获引用方式,=
则表示采用值捕获方式。例如,我们可以重写传递给find_if的lambda:
//sz为隐式捕获,值捕获方式
wc = find_if (words.begin(), words.end(),
[=](const string &s){return s.size()>=sz;});
如果我们希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐
式捕获和显式捕获:
void biggies (vector&words,
vector:size_type sz,
ostream &os = cout,char c =' ')
{
//其他处理与前例一样
//os隐式捕获,引用捕获方式;c显式捕获,值捕获方式
for_each (words.begin(), words.end(),
[& , c](const string &s)(os << s <
当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=。此符号指定了默认捕获方式为引用或值。
当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。即,如果隐式捕获是引用方式(使用了&),则显式捕获命名变量必须采用值方式,因此不能在其名字前使用&。类似的,如果隐式捕获采用的是值方式(使用了=),则显式捕获命名变量必须采用引用方式,即,在名字前使用&。
lambda捕获列表
捕获写法 | 表示含义 |
---|---|
[] |
空捕获列表。lambda不能使用所在函数中的变量。一个lambda只有捕获变量后才能使用它们 |
[name] |
names是一个逗号分隔的名字列表,这些名字都是lambda所在函数的局部变量。默认情况下,捕获列表中的变量都被拷贝。名字前如果使用了&,则采用引用捕获方式 |
[&] |
隐式捕获列表,采用引用捕获方式。ambda体中所使用的来自所在函数的实体都采用引用方式使用 |
[=] |
隐式捕获列表,采用值捕获方式。lambda体将拷贝所使用的来自所在函数的实体的值 |
[&, identifier_list] |
identifier_list是一个逗号分隔的列表,包含0个或多个来自所在函数的变量。这些变量采用值捕获方式,而任何隐式捕获的变量都采用引用方式捕获。identifier_list中的名字前面不能使用& |
[&, identifier_list] |
identifier_list中的变量都采用引用方式捕获,而任何隐式捕获的变量都采用值方式捕获。identifier_list中的名字不能包括this,且这些名字之前必须使用& |
()
可隐藏部分,用来说明接受到的其他形参,这里的形参是不同于捕获需要的局部变量的。
他更多的是规定lambda的使用方式,当缺少参数时,它会表示:你不能这样使用我。
lambda表达式没有缺省值。也不能提供默认初始化。
[]是捕获列表,用于指定lambda表达式中使用的外部变量。可以使用[]来捕获外部变量,包括值捕获和引用捕获。值捕获会将外部变量的值复制到lambda表达式中,而引用捕获则会将外部变量的引用传递给lambda表达式。必须通过[]获取的情况包括:
()是参数列表,用于指定lambda表达式的参数。可以在()中指定lambda表达式的参数类型和名称,多个参数之间用逗号分隔。应当使用()获取的情况包括:
->return type
//错误:不能推断lambda的返回类型
transform(vi.begin ()vi.end(),vi.begin (),
[](int i) {if (i < 0) return -i;else return i;});
编译器推断这个版本的lambda返回类型为void,但它返回了一个int值。
当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型
//错误:不能推断lambda的返回类型
transform(vi.begin ()vi.end(),vi.begin (),
[](int i) -> int {if (i < 0) return -i;else return i;});
在此例中,传递给transform的第四个参数是一个lambda,它的捕获列表是空的,接受
单一int参数,返回一个int值。它的函数体是一个返回其参数的绝对值的if语句。
{}
可变lambda
默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变
一个被捕获的变量的值,就必须在参数列表首加上关键字 mutable
。因此,可变lambda 能省略参数列表:
void fcn3()
{
size t v1=42;//局部变量
//f可以改变它所捕获的变量的值
auto f =[v1]() mutable {return ++vl;};
v1=0;
auto j=f();//j为43
}
一个引用捕获的变量是否(如往常一样)可以修改依赖于此引用指向的是一个cost
类型还是一个非const类型:
void fcn4()
{
s1zetv1=42;//局部变量
//v1是一个非const变量的引用
//可以通过f2中的引用来改变它
auto f2 =[&v1] {return ++v1;};
v1=0:
auto j=f2();//j为1
}
lambda 表达式是一种C++11引入了lambda表达式,它是一种匿名函数,可以在代码中直接定义和使用,不需要像传统函数一样需要先定义再调用。Lambda表达式的语法形式为:[capture list] (parameter list) -> return type { function body }。
其中,capture list是捕获列表,用于指定lambda表达式中使用的外部变量;parameter list是参数列表,用于指定lambda表达式的参数;return type是返回类型,用于指定lambda表达式的返回值类型;function body是函数体,用于指定lambda表达式的具体实现。
下面是一个lambda表达式的使用示例:
auto sum = [](int a, int b) -> int { return a + b; }; int result = sum(1, 2); // result = 3
在这个示例中,我们定义了一个lambda表达式,它接受两个int类型的参数,返回它们的和。然后我们使用auto关键字将这个lambda表达式赋值给一个变量sum,最后调用sum函数计算1和2的和,将结果赋值给result变量。
Lambda表达式在C++中的应用场景包括: