lambda的用途:
– .在算法头文件包含了许多方便使用的函数(例如for_each,transform),这些函数的操作参数中会调用相应自定义的函数句柄实现用户自定义处理。
– .对于类中的部分短小成员函数实现可以使用lambda,这会是的代码量较少,方便阅读。
1.Lambda函数的格式
[捕捉列表] (参数) mutable -> 返回值类型 {函数体}
其中,
[]是lambda的引出符,捕捉列表能够捕捉上下文中的变量,来供lambda函数使用:
[var] 表示以值传递方式捕捉变量var
[=] 表示值传递捕捉所有父作用域变量
[&var] 表示以引用传递方式捕捉变量var
[&] 表示引用传递捕捉所有父作用域变量
[this] 表示值传递方式捕捉当前的this指针
还有一些组合:
[=,&a] 表示以引用传递方式捕捉a,值传递方式捕捉其他变量
注意:捕捉列表不允许变量重复传递,如:[=,a]、[&,&this],会引起编译时期的错误
参数列表与普通函数的参数列表一致。如果不需要传递参数,可以联连同()一同省略。
mutable 可以取消Lambda的常量属性,因为Lambda默认是const属性;multable仅仅是让Lamdba函数体修改值传递的变量,但是修改后并不会影响外部的变量。
->返回类型如果是void时,可以连->一起【省略】,如果返回类型很明确,可以省略,让编译器自动推倒类型。
函数体和普通函数一样,除了可以使用参数之外,还可以使用捕获的变量。
int main(int argc, char* argv[])
{
int a = 5, b = 7;
auto total = [](int x, int y)->int {return x + y; }; //接受两个参数
cout << total(a, b) << endl; //12
auto fun1 = [=] {return a + b; }; //值传递捕捉父作用域变量
cout << fun1() << endl; //12
auto fun2 = [&](int c) {b = a + c; a = 1; }; //省略了返回值类型,引用捕获所有
fun2(3); //1 8
cout << a << " " << b << endl;
a = 5; b = 7; //被修改后,重新赋值
auto fun3 = [=, &b](int c) mutable {b = a + c; a = 1; }; //以值传递捕捉的量,在函数体里如果要修改,要加mutaple,因为默认const修饰
fun3(3);
cout << a << " " << b << endl; //5,8
a = 5; b = 7; //被修改后,重新赋值
auto fun4 = [=](int x, int y) mutable->int {a += x; b += y; return a + b; };
int t = fun4(10, 20);
cout << t << endl; //42
cout << a << " " << b << endl; //5 7
return 0;
}
块作用域以外的Lambda函数捕捉列表必须为空,因此这样的函数除了语法上的不同,和普通函数区别不大。
块作用域以内的Lambda函数仅能捕捉块作用域以内的自动变量,捕捉任何非此作用域或非自动变量(静态变量),都会引起编译器报错。改为引用依旧会报错。
2.Lambda函数与仿函数的关系
仿函数就是实现Lambda函数一种方式,编译器通常会把Lambda函数转换为一个放函数对象。在C++11中,Lambda函数被广泛使用,很多仿函数被取代。
class Price
{
private:
float _rate;
public:
Price(float rate) :_rate(rate) {}
float operator()(float price)
{
return price * (1 - _rate / 100);
}
};
int main(int argc, char* argv[])
{
float rate = 5.5f;
Price c1(rate);
auto c2 = [rate](float price)->float {return price * (1 - rate / 100); };
float p1 = c1(3699); //仿函数
float p2 = c2(3699); //Lambda函数
return 0;
}
3.lambda函数的常量性以及mutable关键字
C++11中,默认情况下lambda函数是一个const函数。
#include <iostream>
using namespace std;
class const_val_lambda
{
public:
const_val_lambda(int v) : m_nVal(v)
{}
public:
void operator() () const
{
// m_nVal = 3; /*注意:常量成员函数*/
}
void ref_const_Fun(int& nValue) const
{
nValue = 100;
}
private:
int m_nVal;
};
void main()
{
int val = 10;
// 编译失败!在const的lambda中修改常量
// auto const_val_lambda = [=]() { val = 3;}; // 不能在非可变 lambda 中修改按值捕获
// 非const的lambda,可以修改常量数据
auto mutable_val_lambda = [=]() mutable{ val = 3; };
// 依然是const的lambda,不过没有改动引用本身
auto const_ref_lambda = [&] { val = 3; };
// 依然是const的lambda,通过参数传递val
auto const_param_lambda = [&](int varA) { varA = 3;};
const_param_lambda(val);
}
int main(int argc, char* argv[])
{
int j = 12;
auto by_val = [=] {return j + 1; };
auto by_ref = [&] {return j + 1; };
cout << by_val() << endl; //13
cout << by_ref() << endl; //13
j++;
cout << by_val() << endl; //13
cout << by_ref() << endl; //14
return 0;
}
j++了之后调用值传递结果依旧是12,原因是,值传递j被视为一个常量,一旦初始化,就不会再修改(可以认为是一个和父作用域中j同名的常量),而再引用捕捉中,j仍然是父作用域中的值。
4.lambda函数与函数指针
lambda的类型并非简单函数指针类型或自定义类型。lambda类型被定义为“闭包”的类,而每一个lambda表达式则会产生一个闭包类型的临时对象,所以lambda函数并非函数指针。但是,C++11标准却允许lambda表达式向函数指针的转换,前提是lambda函数没有捕捉任何变量,且函数指针所示的函数原型,必须跟lambda函数有着相同的调用方式。
#include <iostream>
using namespace std;
void main()
{
int girs = 3, boys = 4;
auto totalChild = [](int x, int y)->int{ return x + y; };
typedef int (*pFunAll)(int x, int y);
typedef int (*pFunOne)(int x);
pFunAll funAll;
// funAll = totalChild; // 编译失败!
pFunOne funOne;
// funOne = totalChild; //编译失败!参数必须一致
decltype(totalChild) allPeople = totalChild; // 需通过decltype获得lambda的类型
// decltype(totalChild) totalPeople = funAll; // 编译失败,指针无法转换lambda
}
第 12 行,编译错误信息如下:
error C2440: “=”: 无法从“`anonymous-namespace’::”转换为“pFunAll”
没有可用于执行该转换的用户定义的转换运算符,或者无法调用该运算符
MSVC10环境下,第一步编译不通过。
关于此问题参见文章《在 MSVC10 下,將 lambda expression 轉換成 C 的 function pointer》
第 15 行 编译失败,参数不一致
第 18 行 编译失败,函数指针转换为lambda也是不成功的。
值得注意的是,可以通过decltype的方式获取lambda函数的类型。
参考
1.https://www.cnblogs.com/WindSun/p/11182276.html
2.https://www.cnblogs.com/Braveliu/p/4231818.html
3.https://www.deeplearn.me/65.html