转自: https://blog.csdn.net/u 010984552/article/details/53634513
一段简单的Code
我也不是文艺的人。 关于Lambda的历史,以及Lambda和c的关系不太清楚。 技术人员讲究拿着代码说话。
复制代码就是:
#includeiostream
用户命名空间STD;
int main () )
{
int a=1;
int b=2;
auto func=[=,b][intc]-int{returnb=ac; (;
返回0;
}
第一次看到这个代码的时候,我直接很乱,直接不知道啊。 上面的代码,如果你理解了,下面的内容那时复习了; 如果不明白了,接下来和我一起总结吧。
基本语法
简单来说,Lambda函数是一个函数,其语法定义如下:
复制代码就是:
“捕获”可移植返回类型{ statement }
1.[捕获] :捕获列表。 捕获列表始终显示在Lambda函数的开头。 其实,[]是Lambda引出子。 编译器根据该引出子判断下一个代码是否是Lambda函数。 捕获列表捕获上下文中的变量以供Lambda函数使用。
2.(参数) :参数列表。 与常规函数的参数列表匹配。 如果不需要参数传递,可以将其与括号「()”一起省略;
3.mutable:mutable修饰符。 缺省情况下,Lambda函数始终是const函数,mutable可以取消常量性。 使用此修饰符时,不能省略参数列表(即使参数为空)。
4.-返回类型:返回类型。 将函数的返回类型声明为跟踪返回类型。 我们不需要返回值时,也可以和符号“-”一起省略。 当返回值的类型明确时,也可以省略该部分而使编译器导出返回值的类型;
5.{statement} :函数体。 内容与常规函数相同,但不仅可以使用参数,还可以使用捕获的所有变量。
与常规函数的最大区别在于,Lambda函数不仅可以使用参数,还可以从捕获列表中访问某些上下文中的数据。 特别是,捕获列表描述上下文中的哪些数据可用于Lambda,以及如何使用(传递值或传递引用)。 语法上,[]括起来的是捕获列表,捕获列表由多个捕获项目组成,并用逗号分隔。 捕获列表具有以下格式:
1.[var]表示通过值的传递方法捕捉变量var;
2.[=]值的传递方法为所有父范围的变量(包括this );
3.[var]参考传递捕获变量var;
4.[]引用传递方式捕获所有父范围的变量(包括this );
5.[this]表示传递值的方法会捕获当前的this指针。
上面介绍了作为包含Lambda函数的语句块的父范围。 通俗地说,就是包含Lambda的“{}”代码块。 上面的捕获列表还可以按如下方式组合:
1.[=,a,b]表示通过引用捕捉变量a和b,通过值传递捕捉所有其他变量。
2.[,a,this]表示通过值传递捕捉变量a和this,通过引用传递捕捉所有其他变量。
但是,捕获列表不允许重复传递变量。 以下例子是典型的重复,会导致编译时期的错误。 例如:
3.[=,a]此处按值传递捕捉所有变量,但如果重复捕捉a,则会报告错误。
4.[,this]这里已经通过引用传递捕获了所有变量。 另外,捕捉this也是反复的。
Lambda的使用
关于使用Lambda,老实说,我什么也没说。 就我个人而言,没有Lambda之前的c,我们也用得那么好。 我对缺少Lambda的c没有意见,但是现在有Lambda表达式的话,写代码会更方便。 不知道您是否记得C STL库中的仿射函数对象,仿射函数希望仿射函数对于普通函数来说可以具有初始化状态。 当声明仿射函数对象时,这些初始化状态由参数指定,并通常存储在仿射函数对象的专用变量中。 在c中,对于要求具有状态的函数,通常使用伪函数实现,如以下代码所示:
复制代码就是:
#includeiostream
用户命名空间STD;
类型编号
{
add=0,
子,
mul、
r> divi}type;
class Calc
{
public:
Calc(int x, int y):m_x(x), m_y(y){}
int operator()(type i)
{
switch (i)
{
case add:
return m_x + m_y;
case sub:
return m_x - m_y;
case mul:
return m_x * m_y;
case divi:
return m_x / m_y;
}
}
private:
int m_x;
int m_y;
};
int main()
{
Calc addObj(10, 20);
cout<<addObj(add)<<endl; // 发现C++11中,enum类型的使用也变了,更“强”了
return 0;
}
现在我们有了Lambda这个利器,那是不是可以重写上面的实现呢?看代码:
复制代码代码如下:
#include<iostream>
using namespace std;
typedef enum
{
add = 0,
sub,
mul,
divi
}type;
int main()
{
int a = 10;
int b = 20;
auto func = [=](type i)->int {
switch (i)
{
case add:
return a + b;
case sub:
return a - b;
case mul:
return a * b;
case divi:
return a / b;
}
};
cout<<func(add)<<endl;
}
显而易见的效果,代码简单了,你也少写了一些代码,也去试一试C++中的Lambda表达式吧。
关于Lambda那些奇葩的东西
看以下一段代码:
复制代码代码如下:
#include<iostream>
using namespace std;
int main()
{
int j = 10;
auto by_val_lambda = [=]{ return j + 1; };
auto by_ref_lambda = [&]{ return j + 1; };
cout<<"by_val_lambda: "<<by_val_lambda()<<endl;
cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
++j;
cout<<"by_val_lambda: "<<by_val_lambda()<<endl;
cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
return 0;
}
程序输出结果如下:
复制代码代码如下:
by_val_lambda: 11
by_ref_lambda: 11
by_val_lambda: 11
by_ref_lambda: 12
你想到了么???那这又是为什么呢?为什么第三个输出不是12呢?
在by_val_lambda中,j被视为一个常量,一旦初始化后不会再改变(可以认为之后只是一个跟父作用域中j同名的常量),而在by_ref_lambda中,j仍然在使用父作用域中的值。所以,在使用Lambda函数的时候,如果需要捕捉的值成为Lambda函数的常量,我们通常会使用按值传递的方式捕捉;相反的,如果需要捕捉的值成成为Lambda函数运行时的变量,则应该采用按引用方式进行捕捉。
再来一段更晕的代码:
复制代码代码如下:
#include<iostream>
using namespace std;
int main()
{
int val = 0;
// auto const_val_lambda = [=](){ val = 3; }; wrong!!!
auto mutable_val_lambda = [=]() mutable{ val = 3; };
mutable_val_lambda();
cout<<val<<endl; // 0
auto const_ref_lambda = [&]() { val = 4; };
const_ref_lambda();
cout<<val<<endl; // 4
auto mutable_ref_lambda = [&]() mutable{ val = 5; };
mutable_ref_lambda();
cout<<val<<endl; // 5
return 0;
}
这段代码主要是用来理解Lambda表达式中的mutable关键字的。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。按照规定,一个const的成员函数是不能在函数体内修改非静态成员变量的值。例如上面的Lambda表达式可以看成以下仿函数代码:
复制代码代码如下:
class const_val_lambda
{
public:
const_val_lambda(int v) : val(v) {}
void operator()() const { val = 3; } // 常量成员函数
private:
int val;
};
对于const的成员函数,修改非静态的成员变量,所以就出错了。而对于引用的传递方式,并不会改变引用本身,而只会改变引用的值,因此就不会报错了。都是一些纠结的规则。慢慢理解吧。
总结
对于Lambda这种东西,有的人用的非常爽,而有的人看着都不爽。仁者见仁,智者见智。不管怎么样,作为程序员的你,都要会的。这篇文章就是用来弥补自己对C++ Lambda表达式的认知不足的过错,以免以后在别人的代码中看到了Lambda,还看不懂这种东西,那就丢大人了。