C++11:lambda表达式
lambda基础使⽤
lambda 表达式(lambda expression)是⼀个匿名函数,lambda表达式基于数学中的 λ 演算得名。
C++11中的lambda表达式⽤于定义并创建匿名的函数对象,以简化编程⼯作。
lambda表达式的基本构成:
① 函数对象参数
[],标识⼀个lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器⾃动⽣成的函数对象类的构造函数的。函数对象参数只能使⽤那些到定义lambda为⽌时lambda所在作⽤范围内可见的局部变量(包括lambda所在类的this)。函数对象参数有以下形式:
空。没有使⽤任何函数对象参数。
=。函数体内可以使⽤lambda所在作⽤范围内所有可见的局部变量(包括lambda所在类的this),并且是值传递⽅式(相当于编译器⾃动为我们按值传递了所有局部变量)。
&。函数体内可以使⽤lambda所在作⽤范围内所有可见的局部变量(包括lambda所在类的this),并且是引⽤传递⽅式(相当于编译器⾃动为我们按引⽤传递了所有局部变量)。
this。函数体内可以使⽤lambda所在类中的成员变量。
a。将a按值进⾏传递。按值进⾏传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a 的拷贝,可以添加mutable修饰符。
&a。将a按引⽤进⾏传递。
a, &b。将a按值进⾏传递,b按引⽤进⾏传递。
=,&a, &b。除a和b按引⽤进⾏传递外,其他参数都按值进⾏传递。
&, a, b。除a和b按值进⾏传递外,其他参数都按引⽤进⾏传递。
② 操作符重载函数参数
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引⽤(如:(&a,&b))两种⽅式进⾏传递。
③ 可修改标⽰符
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,⽽不是值本⾝)。
④ 错误抛出标⽰符
exception声明,这部分也可以省略。exception声明⽤于指定函数抛出的异常,如抛出整数类型的异常,可以使⽤throw(int)
⑤ 函数返回值
->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有⼀处return的地⽅(此时编译器可以⾃动推断出返回值类型)时,这部分可以省略。
⑥ 是函数体
{},标识函数的实现,这部分不能省略,但函数体可以为空。
class Test
{
public:
int i = 0;
void func(int x, int y)
{
auto x1 = []{ return i; };          //err, 没有捕获外部变量
auto x2 = [=]{ return i+x+y; };    //ok, 值传递⽅式捕获所有外部变量
auto x3 = [=]{ return i+x+y; };    //ok, 引⽤传递⽅式捕获所有外部变量
auto x4 = [this]{ return i; };      //ok, 捕获this指针
auto x5 = [this]{ return i+x+y; };  //err, 没有捕获x, y
auto x6 = [this, x, y]{ return i+x+y; };//ok, 捕获this指针, x, y
auto x7 = [this]{ return i++; };        //ok, 捕获this指针, 并修改成员的值
}
};
int main()
{
int a = 0, b = 1;
auto f1 = []{ return a; };      //err, 没有捕获外部变量
auto f2 = [=]{ return a; };    //ok, 值传递⽅式捕获所有外部变量
auto f3 = [=]{ return a++; };  //err, a是以赋值⽅式捕获的,⽆法修改
auto f4 = [=]() mutable { return a++; };  //ok, 加上mutable修饰符后,可以修改按值传递进来的拷贝
auto f5 = [&]{ return a++; };              //ok, 引⽤传递⽅式捕获所有外部变量, 并对a执⾏⾃加运算
auto f6 = [a]{ return a+b; };              //err, 没有捕获变量b
auto f7 = [a,&b]{ return a+(b++); };        //ok, 捕获a, &b
auto f8 = [=,&b]{ return a+(b++); };        //ok, 捕获所有外部变量,&b
return0;
}
值传递和引⽤传递区别:
#include <iostream>
using namespace std;
int main()
{
int j = 12;
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;
return0;
}
运⾏结果如下:
第3次调⽤结果还是13,原因是由于by_val_lambda中,j被视为了⼀个常量,⼀旦初始化后不会再改变。
lambda与仿函数
#include <iostream>
using namespace std;
class MyFunctor
{
public:
MyFunctor(int tmp) : round(tmp) {}
int operator()(int tmp) { return tmp + round; }
private:
int round;
};
int main()
{
//仿函数
int round = 2;
MyFunctor f1(round);//调⽤构造函数
cout << "result1 = " << f1(1) << endl; //operator()(int tmp)
/
/lambda表达式
auto f2 = [=](int tmp) -> int { return tmp + round; } ;
cout << "result2 = " << f2(1) << endl;
return0;
}
运⾏结果如下:
通过上⾯的例⼦,我们看到,仿函数以round初始化类,⽽lambda函数也捕获了round变量,其它的,如果在参数传递上,两者保持⼀致。
除去在语法层⾯上的不同,lambda和仿函数有着相同的内涵——都可以捕获⼀些变量作为初始化状态,并接受参数进⾏运⾏。
⽽事实上,仿函数是编译器实现lambda的⼀种⽅式,通过编译器都是把lambda表达式转化为⼀个仿函数对象。因此,在C++11
中,lambda可以视为仿函数的⼀种等价形式。
lambda类型
lambda表达式的类型在C++11中被称为“闭包类型”,每⼀个lambda表达式则会产⽣⼀个临时对象(右值)。因此,严格地将,lambda函数并⾮函数指针。
不过C++11标准却允许lambda表达式向函数指针的转换,但提前是lambda函数没有捕获任何变量,且函数指针所⽰的函数原型,必须跟lambda函数函数有着相同的调⽤⽅式。
int main()
{
//使⽤std::function和std::bind来存储和操作lambda表达式
function<int(int)> f1 = [](int a) { return a; };
function<int()> f2 = bind([](int a){ return a; }, 123);
cout << "f1 = " << f1(123) << endl;
cout << "f2 = " << f2() << endl;
auto f3 = [](int x, int y)->int{ return x + y; }; //lambda表达式,没有捕获任何外部变量
typedef int (*PF1)(int x, int y);  //函数指针类型
typedef int (*PF2)(int x);lambda编程
PF1 p1;    //函数指针变量
p1 = f3;    //ok, lambda表达式向函数指针的转换
cout << "p1 = " << p1(3, 4) << endl;
PF2 p2;
p2 = f3;    //err, 编译失败,参数必须⼀致
decltype(f3) p3 = f3;  // 需通过decltype获得lambda的类型
decltype(f3) p4 = p1;  // err 编译失败,指针⽆法转换为lambda
return0;
}
lambda优势
#include <vector>
#include <algorithm> //std::for_each
#include <iostream>
using namespace std;
vector<int> nums;
vector<int> largeNums;
class LNums
{
public:
LNums(int u): ubound(u){} //构造函数
void operator () (int i) const
{//仿函数
if (i > ubound)
{
largeNums.push_back(i);
}
}
private:
int ubound;
};
int main()
{
//初始化数据
for(auto i = 0; i < 10; ++i)
{
nums.push_back(i);
}
int ubound = 5;
/
/1、传统的for循环
for (auto itr = nums.begin(); itr != d(); ++itr)
{
if (*itr > ubound)
if (*itr > ubound)
{
largeNums.push_back(*itr);
}
}
//2、使⽤仿函数
for_each(nums.begin(), d(), LNums(ubound));
/
/3、使⽤lambda函数和算法for_each
for_each(nums.begin(), d(), [=](int i)
{
if (i > ubound)
{
largeNums.push_back(i);
}
}
);
//4、遍历元素
for_each(largeNums.begin(), d(), [=](int i)
{
cout << i << ", ";
}
);
cout << endl;
return0;
}
lambda表达式的价值在于,就地封装短⼩的功能闭包,可以及其⽅便地表达出我们希望执⾏的具体操作,并让上下⽂结合更加紧密。
参考资料:
1、
2、

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。