以前,在lambda表达式没有进入标准的时候,对容器的遍历等涉及到使用函数指针的情况,一般人会懒得使用std::for_each,也许只是一个短短的几句话,却要单独写个函数,或函数对象,写的代码反而不如自己用for循环来的快。
但是,C++11引入了lambda表达式后,一切都变的简单了!


1.lambda表达式

lambda表达式是一个匿名函数,用它可以非常方便的表示一个函数对象,先简单说一下lambda表达式,下面这张图表示了C++11中lambda表达式的写法


  1. Lambda表达式的引入标志,在‘[]’里面可以填入‘=’或‘&’表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)的数据时以什么方式捕获的,‘&’表示一引用的方式;‘=’表明以值传递的方式捕获,除非专门指出。

  2. Lambda表达式的参数列表

  3. Mutable 标识(可以没有)

  4. 异常标识(可以没有)

  5. 返回值,如果没有,可以不写

  6. “函数”体,也就是lambda表达式需要进行的实际操作

下面看看几个lambda表达式的例子

  1. void print(int a){……}  
void print(int a){……}
上面函数lambda表达式为:

  1. [](int a){……}  
[](int a){……}
上面这个是无返回值的例子,下面这个是有返回值的例子
  1. int add(int a,int b){……}  
int add(int a,int b){……}
上面函数lambda表达式为:
  1. [](int a,int b)->int{……}  
[](int a,int b)->int{……}
当需要引入其他变量的时候,如有个类的成员变量需要引用或者函数局部变量这种情况下可以显示声明需要引入的变量
如成员变量:
double m_result;
函数:

  1. void foo(int a,double b)  
  2. {  
  3.     ……  
  4.     use(m_result);  
  5. }  
void foo(int a,double b)
{
    ……
    use(m_result);
}
其lambda表达式可以表示为
  1. [m_result](int a,int b){……use(m_result);}  
[m_result](int a,int b){……use(m_result);}
如果lambda表达式需要修改m_result的值,可以以引用方式传递进去
  1. [&m_result](int a,int b){……m_result = 12.1;……}  
[&m_result](int a,int b){……m_result = 12.1;……}
如果要传入的参数很多,或者干脆在这个作用域里的所有参数都想用到,可以直接在中括号里使用”=“或”&“
如下例子,g_bb为一个全局变量,fun3的lambda表达式把所有内容以引用方式传入:
  1. double g_bb = 11.2;  
  2. void foo1()  
  3. {  
  4.     auto f_add = [&](int a,int b)->int{return a+b;};  
  5.     std::cout<<f_add(1,2);//3  
  6.     std::cout<<std::endl;  
  7.       
  8.     double aa = 5.0;  
  9.     auto fun = [aa]()->double{return aa+3;};//此时aa不能进行赋值操作如:aa=7;  
  10.     std::cout<<fun();  
  11.     std::cout<<"  aa:"<<aa<<std::endl;//8 aa:5  
  12.     auto fun2 = [&aa]()->double{aa = 7.0;return aa+3;};//此时aa以引用方式传入,可以进行赋值操作如:aa=7,同时修改aa的值;  
  13.     std::cout<<fun2();  
  14.     std::cout<<"  aa:"<<aa<<std::endl;//10  aa:7  
  15.     auto fun3 = [&]()->double{aa = 8.0;g_bb = 15;return aa+3;};//此时aa可以进行赋值操作如:aa=7;,其他在作用域范围的变量都可以以引用方式调用  
  16.     std::cout<<fun3();  
  17.     std::cout<<"  aa:"<<aa<<" g_bb:"<<g_bb<<std::endl;//11  aa:8  
  18. }  
double g_bb = 11.2;
void foo1()
{
    auto f_add = [&](int a,int b)->int{return a+b;};
    std::cout<<f_add(1,2);//3
    std::cout<<std::endl;
    
    double aa = 5.0;
    auto fun = [aa]()->double{return aa+3;};//此时aa不能进行赋值操作如:aa=7;
    std::cout<<fun();
    std::cout<<"  aa:"<<aa<<std::endl;//8 aa:5
    auto fun2 = [&aa]()->double{aa = 7.0;return aa+3;};//此时aa以引用方式传入,可以进行赋值操作如:aa=7,同时修改aa的值;
    std::cout<<fun2();
    std::cout<<"  aa:"<<aa<<std::endl;//10  aa:7
    auto fun3 = [&]()->double{aa = 8.0;g_bb = 15;return aa+3;};//此时aa可以进行赋值操作如:aa=7;,其他在作用域范围的变量都可以以引用方式调用
    std::cout<<fun3();
    std::cout<<"  aa:"<<aa<<" g_bb:"<<g_bb<<std::endl;//11  aa:8
}
输出:

  1. 3  
  2. 8  aa:5  
  3. 10  aa:7  
  4. 11  aa:8 g_bb:15  
3
8  aa:5
10  aa:7
11  aa:8 g_bb:15

一般也是以这种方式来写[&],简单明了。
lambda表达式先说到这后面在讲std::for_each和std::transform时会有更多例子。

2.std::for_each

std::foreach是很经典的算法,但是由于需要用到函数对象,有时候还不如直接for循环方便,例如我有个vector,我要打印出来看看里面有什么内容,经常下意识的就直接成:

  1. std ::vector <double>v ;  
  2. v . push_back ( 3);  
  3. v . push_back ( 1. 666);  
  4. v . push_back ( 4. 5);  
  5. v . push_back ( 6. 7);  
  6. for( std ::vector <double>::iterator i =v . begin (); i !=v . end (); ++i )  
  7. {  
  8.     std ::cout <<*i <<",";  
  9. }  
std ::vector <double>v ;
v . push_back ( 3);
v . push_back ( 1. 666);
v . push_back ( 4. 5);
v . push_back ( 6. 7);
for( std ::vector <double>::iterator i =v . begin (); i !=v . end (); ++i )
{
    std ::cout <<*i <<",";
}

std :: vector < double >:: iterator是多么的碍眼的。
还好,C++11把auto升了级,上面那个代码会变成

  1. for( autoi =v . begin (); i !=v . end (); ++i )  
for( autoi =v . begin (); i !=v . end (); ++i )

用std::foreach(),上面这个就变成

  1. std ::for_each ( v . begin (), v . end (),[ &]( double d ){ std ::cout <<d <<",";});  
std ::for_each ( v . begin (), v . end (),[ &]( double d ){ std ::cout <<d <<",";});

于是,以后凡是要遍历容器,且代码不太长,都可以使用std::for_each加lambda表达式方便实现。

C++11 for循环的新写法可以将上述写成:
for(auto b : v){ //或者for(const double& b : v)
	std::cout<<b<<std::endl;
}

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐