代码改变世界

C++11之lambda表达式

2016-12-11 16:41  jiayayao  阅读(1762)  评论(0编辑  收藏  举报

  lambda表达式源于函数式编程的概念,它可以就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。lambda表达式的类型在C++11中被称为“闭包类型”,也可以理解为是一个仿函数(带operator()类),其语法形式如下:

  [capture] (params) opt -> ret {body;};

  capture: 捕获列表;

  params: 参数列表;

  opt: 函数选项;

  ret: 返回值类型;

  body: 函数体。

  很多时候,lambda表达式的返回值是非常明显的,C++11中允许省略lambda表达式的返回值定义,即去掉“-> ret”部分,这样编译器就会自动推导出返回值类型。一个简单的lambda表达式如下:  

#include "stdafx.h"
#include<iostream>// std::cout

int main()
{
    auto f = [](int a, int b) -> int {return (a*b); };
    std::cout << f(3, 4) << std::endl; // output:12

    return 0;
}

  lambda表达式中的捕获字段非常有用,捕获的意思是捕获外部变量在lambda函数体内部使用,有以下几种不同的捕获方式:

  []:不捕获外部的任何变量,如果在函数体内使用外部变量就会报错;

  [&]:以引用的方式捕获外部的所有变量,如果在函数体内改变外部变量值,外部变量值就会随之改变;

  [=]:以值的方式捕获外部的所有变量,如果在函数体内改变“外部变量值”,外部变量值不会改变;

  [=, &foo]:以值得方式捕获外部所有变量,并以引用的方式捕获foo变量;

  [bar]:以值得方式捕获bar变量,其他外部变量不捕获;

  [this]:捕获当前类中的this指针,让lambda表达式拥有和当前类成员函数同样的访问权限。如果已经使用了&或者=,则this是默认添加的。

  注意按值捕获和按引用捕获的区别,按值捕获将在lambda表达式中用到的外部变量进行了一份拷贝并且保存到了lambda闭包类型中,如果之后对所用到的外部变量进行修改,lambda表达式中用到的变量是不会改变的,因为该变量只是一份拷贝;按引用捕获则是直接对变量的值进行读取或操作,如代码所示:

#include "stdafx.h"
#include<iostream>// std::cout

int main()
{
    int numA = 0;
    int numB = 0;
    {
        auto f1 = [] {return numA; };// error,没有捕获外部变量
        auto f2 = [&numB] {return (--numB); };
        f2();
        std::cout << numB << std::endl;// -1
    }

    int numC = 100;
    int numD = 100;
    auto f3 = [=]() -> int {return (numC*numD); };
    numD = 99;
    std::cout << f3() << std::endl; // output:10000

    return 0;
}
  有了lambda表达式,我们在使用for循环或者std::for_each的时候,就可以改写成如下形式:
#include "stdafx.h"
#include<iostream>// std::cout
#include<list>
#include <algorithm>
int main()
{
    std::list<int> l = { 1, 3, 5, 87, 64, 33, 0, -1, -100};
    int nCount = 0;
    std::for_each(l.begin(), l.end(), [&nCount](int val) {if (val > 50) nCount++; });
    std::cout << "The number larger than 50 in l is: " << nCount << std::endl;

    return 0;
}

  总结一下,lambda表达式看起来和Java里边的匿名函数比较像,使用lambda表达式后,可以使程序变得非常简洁。但是,如果使用的lambda表达式包含的内容过多,或者lambda表达式的实体是好多地方都会用到的功能性函数,还是单独列出一个函数比较方便。