C++11中的匿名函数(lambda)

 C++11提供了对匿名函数的支持,称为Lambda函数(也叫Lambda表达式)。

 下面举了几个Lambda函数的例子:

[](int x, int y) { return x + y; } // 隐式返回类型
[](int& x) { ++x; }   // 没有return语句 -> lambda 函数的返回类型是'void'
[]() { ++global_x; }  // 没有参数,仅访问某个全局变量
[]{ ++global_x; }     // 与上一个相同,省略了()

指定返回类型:

[](int x, int y) -> int { int z = x + y; return z; }

在这个例子中创建了一个临时变量z来存储中间值。和普通函数一样,这个中间值不会保存到下次调用。什么也不返回的Lambda函数可以省略返回类型,而不需要使用 -> void 形式。

lambda表达式的语法定义如下:
  [capture] (parameters) mutable ->return-type {statement};
  (1) [capture]: 捕捉列表。捕捉列表总是出现在lambda函数的开始处。实质上,[]是lambda引出符(即独特的标志符)
  编译器根据该引出符判断接下来的代码是否是lambda函数
  捕捉列表能够捕捉上下文中的变量以供lambda函数使用
  捕捉列表由一个或多个捕捉项组成,并以逗号分隔,捕捉列表一般有以下几种形式:
  <1> [var] 表示值传递方式捕捉变量var
  <2> [=] 表示值传递方式捕捉所有父作用域的变量(包括this指针)
  <3> [&var] 表示引用传递捕捉变量var
  <4> [&] 表示引用传递捕捉所有父作用域的变量(包括this指针)
  <5> [this] 表示值传递方式捕捉当前的this指针
  <6> [=,&a,&b] 表示以引用传递的方式捕捉变量 a 和 b,而以值传递方式捕捉其他所有的变量
  <7> [&,a,this] 表示以值传递的方式捕捉 a 和 this,而以引用传递方式捕捉其他所有变量
  备注:父作用域是指包含lambda函数的语句块
  另外,需要注意的是,捕捉列表不允许变量重复传递。下面的例子就是典型的重复,会导致编译错误:
  [=, a] 这里 = 已经以值传递方式捕捉了所有的变量,那么再捕捉 a 属于重复
  [&,&this] 这里 & 已经以引用传递方式捕捉了所有变量,那么再捕捉 this 属于重复
  (2)(parameters):参数列表。与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号()一起省略
  (3)mutable : mutable修饰符。默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性(后面有详解)
  在使用该修饰符时,参数列表不可省略(即使参数为空)
  (4)->return-type : 返回类型。用追踪返回类型形式声明函数的返回类型。
  出于方便,不需要返回值的时候也可以连同符号->一起省略
  此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导
  (5){statement} : 函数体。内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量
  在lambda函数的定义中,参数列表和返回类型都是可选的部分,而捕捉列表和函数体都可能为空
  那么,在极端情况下,C++11中最为简单的lambda函数只需要声明为:
  [] {};就可以了.

 Lambda函数可以引用在它之外声明的变量。这些变量的集合叫做一个闭包,闭包被定义在Lambda表达式声明中的方括号[]内。这个机制允许这些变量被按值或按引用捕获。

引用示例:

float f0 = 1.0f;
float f1 = 10.0f;
std::cout << [=, &f0](float a) { return f0 += f1 + std::abs(a); } (-3.5);  // 输出结果: 14.5
std::cout << '\n' << f0 << '\n';   // 输出结果: 14.5

lambda函数是一个依赖于实现的函数对象类型,这个类型的名字只有编译器知道。如果用户想把lambda函数做为一个参数来传递吗,那么形参的类型必须是模板类型或者必须能创建一个std::function类似的对象去捕获lambda函数。使用 auto关键字可以帮助存储lambda函数,

auto my_lambda_func = [&](int x) { /*...*/ };
auto my_onheap_lambda_func = new auto([=](int x) { /*...*/ });

匿名函数存储在变量,数组或vector中,并把它们当做命名参数来传递:

#include<functional>
#include<vector>
#include<iostream>
double eval(std::function<double(double)> f, double x = 2.0){return f(x);}
int main()
{
     std::function<double(double)> f0    = [](double x){return 1;};
     auto                          f1    = [](double x){return x;};
     decltype(f0)                  fa[3] = {f0,f1,[](double x){return x*x;}};
     std::vector<decltype(f0)>     fv    = {f0,f1};
     fv.push_back                  ([](double x){return x*x;});
     for(int i=0;i<fv.size();i++)  std::cout << fv[i](2.0) << "\n";
     for(int i=0;i<3;i++)          std::cout << fa[i](2.0) << "\n";
     for(auto &f : fv)             std::cout << f(2.0) << "\n";
     for(auto &f : fa)             std::cout << f(2.0) << "\n";
     std::cout << eval(f0) << "\n";
     std::cout << eval(f1) << "\n";
     return 0;
}

一个没有指定任何捕获的lambda函数,可以显式转换成一个具有相同声明形式函数指针。所以,像下面这样做是合法的:

auto a_lambda_func = [](int x) { /*...*/ };
void(*func_ptr)(int) = a_lambda_func;
func_ptr(4); //calls the lambda.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值