c++ 一文搞懂Lambda表达式(匿名函数)

使用 STL 时,往往会大量用到函数对象,为此要编写很多函数对象类。有的函数对象类只用来定义了一个对象,而且这个对象也只使用了一次,编写这样的函数对象类就有点浪费。

而且,定义函数对象类的地方和使用函数对象的地方可能相隔较远,看到函数对象,想要查看其 operator() 成员函数到底是做什么的也会比较麻烦。

对于只使用一次的函数对象类,能否直接在使用它的地方定义呢?Lambda 表达式能够解决这个问题。使用 Lambda 表达式可以减少程序中函数对象类的数量,使得程序更加优雅

为什么说 lambda 表达式如此激动人心呢?举一个例子。标准 C++ 库中有一个常用算法的库,其中提供了很多算法函数,比如 sort() 和 find()。这些函数通常需要提供一个“谓词函数 predicate function”。所谓谓词函数,就是进行一个操作用的临时函数。比如 find() 需要一个谓词,用于查找元素满足的条件;能够满足谓词函数的元素才会被查找出来。这样的谓词函数,使用临时的匿名函数,既可以减少函数数量,又会让代码变得清晰易读。
比如你代码里有一些小函数,而这些函数一般只被调用一次(比如函数指针),这时你就可以用lambda表达式替代他们,这样代码看起来更简洁些,用起来也方便。

1.Lambda 表达式的定义形式如下:

[外部变量访问方式说明符] (参数表) -> 返回值类型
{
语句块
}

  • 其中,“外部变量访问方式说明符”可以是 =&,表示代码块{}中用到的、定义在代码块{}外面的变量在代码块{}中是否允许被改变=表示不允许,&表示允许。当然,在{}中也可以不使用定义在外面的变量。
  • “->返回值类型”可以省略。

2.合法的Lambda表达式示例:

[=] (int x, int y) -> bool {return x%10 < y%10; }	//{}中用到的外部变量不能改变

//创建了一个临时变量z来存储中间值. 和普通函数一样,这个中间值不会保存到下次调用. 
[](int x, int y) -> int { int z = x + y; return z; }	//显式指定返回类型
[](int x, int y) { return x + y; } // 隐式返回类型

//什么也不返回的Lambda函数可以省略返回类型, 而不需要使用 -> void 形式.
[](int& x) { ++x; }   // 没有return语句 -> lambda 函数的返回类型是'void'

[]() { ++global_x; }  // 没有参数,仅访问某个全局变量
[]{ ++global_x; }     // 与上一个相同,省略了()

3.对于sort函数自定义比较方法

void test() 
{
	vector<int> v = { 1,3,5,2,4,6 };
	std::sort(v.begin(), v.end(), [](int& v1, int& v2)->bool {return v1 < v2; });
}

需要访问外部变量时:

void test() 
{
vector<int> v = { 1,3,5,2,4,6 };
int compTimes = 0;
std::sort(v.begin(), v.end(), [&compTimes](int& v1, int& v2)->bool {compTimes++; return v1 < v2; });
}

当我们想引用一个 lambda 表达式时,我们可以使用auto关键字,例如:

auto lambda = [] () -> int { return val * 100; };

4.具体用法示例及分析

int a[4] = {11, 2, 33, 4};
sort(a, a+4, [=](int x, int y) -> bool { return x%10 < y%10; } );
for_each(a, a+4, [=](int x) { cout << x << " ";} );
  • 这段程的输出结果是: 11 2 33 4
  • 程序第 2 行使得数组 a 按个位数从小到大排序。具体的原理是:sort 在执行过程中,需要判断两个元素 x、y 的大小时,会以x、y作为参数,调用 Lambda 表达式所代表的函数,并根据返回值来判断 x、y 的大小。这样,就不用专门编写一个函数对象类了。
  • 第 3 行,for_each 的第 3 个参数是一个 Lambda表达式。for_each执行过程中会依次以每个元素作为参数调用它,因此每个元素都被输出。
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int total = 0;
    for_each(a, a + 4, [&](int & x) { total += x; x *= 2; });
    cout << total << endl;  //输出 10
    for_each(a, a + 4, [=](int x) { cout << x << " "; });
    return 0;
}
  • 程序的输出结果如下:
    10
    2 4 6 8
  • 第 8 行,[&]表示该 Lambda 表达式中用到的外部变量 total是传引用的,其值可以在表达式执行过程中被改变(如果使用[=],编译无法通过)。该 Lambda 表达式每次被 for_each执行时,都将 a 中的一个元素累加到 total 上,然后将该元素加倍。
实际上,“外部变量访问方式说明符”还可以有更加复杂和灵活的用法。例如:
  • [=, &x, &y]表示外部变量 x、y 的值可以被修改,其余外部变量不能被修改;
  • [&, x, y]表示除 x、y 以外的外部变量,值都可以被修改。
#include <iostream>
using namespace std;
int main()
{   
    int x = 100,y=200,z=300;
    auto ff  = [=,&y,&z](int n) {
        cout <<x << endl;
        y++; z++;
        return n*n;
    };
    cout << ff(15) << endl;
    cout << y << "," << z << endl;
}
  • 程序的输出结果如下:
    100
    225
    201, 301
  • 第 6 行定义了一个变量 ff,ff 的类型是 auto,表示由编译器自动判断其类型(这也是 C++11 的新特性)。本行将一个Lambda 表达式赋值给 ff,以后就可以通过 ff 来调用该 Lambda 表达式了。
  • 第 11 行通过 ff,以 15 作为参数 n 调用上面的 Lambda 表达式。该 Lambda 表达式指明,对于外部变量y、z,可以修改其值;对于其他外部变量,例如 x,不能修改其值。因此在该表达式执行时,可以修改外部变量 y、z 的值,但如果出现试图修改x 值的语句,就会编译出错。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值