C++中Lamda表达式

C++11 的 lambda 表达式规范如下:

(1)        [ capture ] ( params ) mutable exception attribute -> ret { body }

(2)        [ capture ] ( params ) -> ret { body }
(3)        [ capture ] ( params ) { body }
(4)        [ capture ] { body  }

其中

  • (1) 是完整的 lambda 表达式形式,
  • (2) const 类型的 lambda 表达式,该类型的表达式不能改捕获("capture")列表中的值。
  • (3)省略了返回值类型的 lambda 表达式,但是该 lambda 表达式的返回类型可以按照下列规则推演出来:
    • 如果 lambda 代码块中包含了 return 语句,则该 lambda 表达式的返回类型由 return 语句的返回类型确定。
    • 如果没有 return 语句,则类似 void f(...) 函数。
  • 省略了参数列表,类似于无参函数 f()。

mutable 修饰符说明 lambda 表达式体内的代码可以修改被捕获的变量,并且可以访问被捕获对象的 non-const 方法。

exception 说明 lambda 表达式是否抛出异常(noexcept),以及抛出何种异常,类似于void f() throw(X, Y)

attribute 用来声明属性。

另外,capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表,具体解释如下:

  • [a,&b] a变量以值的方式呗捕获,b以引用的方式被捕获。
  • [this] 以值的方式捕获 this 指针。
  • [&] 以引用的方式捕获所有的外部自动变量。
  • [=] 以值的方式捕获所有的外部自动变量。
  • [] 不捕获外部的任何变量。

lambda表达式有如下优点:

1).声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可维护性。

2).简洁:不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率。

3).在需要的时间和地点实现功能闭包,使程序更灵活。

class A  
{  
public:  
    int i_ = 0;  
      
    void func(int x,int y){  
        auto x1 = [] { return i_; };                             //error,没有捕获外部变量  
        auto x2 = [=] { return i_ + x + y; };                  //OK  
        auto x3 = [&] { return i_ + x + y; };               //OK  
        auto x4 = [this] { return i_; };                        //OK  
        auto x5 = [this] { return i_ + x + y; };            //error,没有捕获x,y  
        auto x6 = [this, x, y] { return i_ + x + y; };    //OK  
        auto x7 = [this] { return i_++; };                   //OK  
};  
  
int a=0 , b=1;  
auto f1 = [] { return a; };                            //error,没有捕获外部变量      
auto f2 = [&] { return a++ };                      //OK  
auto f3 = [=] { return a; };                         //OK  
auto f4 = [=] {return a++; };                     //error,a是以复制方式捕获的,无法修改  
auto f5 = [a] { return a+b; };                     //error,没有捕获变量b  
auto f6 = [a, &b] { return a + (b++); };      //OK  
auto f7 = [=, &b] { return a + (b++); };     //OK  

注意的细节:

1.

  一个容易出错的细节是lambda表达式的延迟调用,lambda表达式按值捕获了所有外部变量。在捕获的一瞬间,a的值就已经被复制了。如果希望lambda表达式在调用时能即时访问外部变量,我们应当使用引用方式捕获。



int a = 0;  
auto f = [=] { return a; };  
  
a+=1;  
  
cout << f() << endl;       //输出0  
  
  
int a = 0;  
auto f = [&a] { return a; };  
  
a+=1;  
  
cout << f() <<endl;       //输出1  

2.

  虽然按值捕获的变量值均补复制一份存储在lambda表达式变量中, 修改他们也并不会真正影响到外部,但我们却仍然无法修改它们。

  那么如果希望去修改按值捕获的外部变量,需要显示指明lambda表达式为mutable。

  需要注意:被mutable修饰的lambda表达式就算没有参数也要写明参数列表。
  原因:lambda表达式可以说是就地定义仿函数闭包的“语法糖”。它的捕获列表捕获住的任何外部变量,最终均会变为闭包类型的成员变量。按照C++标准,lambda表达式的operator()默认是const的,一个const成员函数是无法修改成员变量的值的。而mutable的作用,就在于取消operator()的const。


int a = 0;  
auto f1 = [=] { return a++; };                       //error  
auto f2 = [=] () mutable { return a++; };       //OK  

3.

  没有捕获变量的lambda表达式可以直接转换为函数指针,而捕获变量的lambda表达式则不能转换为函数指针。原因可以参考2中的原因。

typedef void(*Ptr)(int*);  
  
Ptr p = [](int* p) { delete p; };              //OK  
Ptr p1 = [&] (int* p) { delete p; };         //error  

最后,两个实际应用到lambda表达式的代码。

std::vector<int> v = { 1, 2, 3, 4, 5, 6 };  
int even_count = 0;  
for_each(v.begin(), v.end(), [&even_count](int val){  
    if(!(val & 1)){  
        ++ even_count;  
    }  
});  
std::cout << "The number of even is " << even_count << std::endl;  
int count = std::count_if( coll.begin(), coll.end(), [](int x){ return x > 10; });  
  
int count = std::count_if( coll.begin(), coll.end(), [](int x){ return x < 10; });  
  
int count = std::count_if( coll.begin(), coll.end(), [](int x){ return x > 5 && x<10; });  



  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值