Chinaunix首页 | 论坛 | 博客
  • 博客访问: 298349
  • 博文数量: 63
  • 博客积分: 814
  • 博客等级: 军士长
  • 技术积分: 700
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-09 15:46
文章分类

全部博文(63)

文章存档

2017年(1)

2016年(4)

2015年(13)

2014年(9)

2012年(3)

2011年(33)

分类: C/C++

2015-01-19 12:03:42

http://blog.csdn.net/booirror/article/details/26973611

为什么需要lambda函数
匿名函数是许多编程语言都支持的概念,有函数体,没有函数名。1958年,lisp首先采用匿名函数,匿名函数最常用的是作为回调函数的值。正因为有这样的需求,c++引入了lambda 函数,你可以在你的源码中内联一个lambda函数,这就使得创建快速的,一次性的函数变得简单了。例如,你可以把lambda函数可在参数中传递给std::sort函数

#include
#include
void abssort(float* x, unsigned N) {
    std::sort(x, x + N,
        // Lambda expression begins
        [](float a, float b) {
            return std::abs(a) < std::abs(b);
        });
}
你可能会问,使用函数对象不是也可以吗?是的,函数对象当然没问题,自己写的回调函数,你可以传个函数指针也没有问题。他们有优点也有缺点。函数对象能维护状态,但语法开销大,而函数指针语法开销小,却没法保存范围内的状态。如果你觉得鱼和熊掌不可兼得,那你可错了。lambda函数结合了两者的优点,让你写出优雅简洁的代码。

基本lambda语法
基本形式如下:
[capture](parameters)->return-type {body}

[]叫做捕获说明符,表示一个lambda表达式的开始。接下来是参数列表,即这个匿名的lambda函数的参数,->return-type表示返回类型,如果没有返回类型,则可以省略这部分。想知道为什么返回类型可以这么表示,这涉及到c++11的另一特性,参见自动类型推导,最后就是函数体部分了。
我们可以这样输出"hello,world"
auto func = [] () { cout << "hello,world"; };
func(); // now call the function

变量捕获与lambda闭包实现
string name;
cin >> name;
[&](){cout << name;}();

lambda函数能够捕获lambda函数外的具有自动存储时期的变量。函数体与这些变量的集合合起来叫闭包。
  • [] 不截取任何变量
  • [&} 截取外部作用域中所有变量,并作为引用在函数体中使用
  • [=] 截取外部作用域中所有变量,并拷贝一份在函数体中使用
  • [=, &foo] 截取外部作用域中所有变量,并拷贝一份在函数体中使用,但是对foo变量使用引用
  • [bar] 截取bar变量并且拷贝一份在函数体重使用,同时不截取其他变量
  • [x, &y] x按值传递,y按引用传递
  • [this] 截取当前类中的this指针。如果已经使用了&或者=就默认添加此选项。
看到这,不禁要问,这魔法般的变量捕获是怎么实现的呢?原来,lambda是通过创建个小类来实现的。这个类重载了操作符(),一个lambda函数是该类的一个实例。当该类被构造时,周围的变量就传递给构造函数并以成员变量保存起来。看起来跟函数对象很相似。

最后,lambda函数的类型是什么呢,答案是std:function。


useful blog 
http://blog.chinaunix.net/uid-20726254-id-3483489.html


------------------------------------------------------------------------------------------------------------------------

C++11标准提供了匿名函数的支持,在《ISO/IEC 14882:2011》(C++11标准文档)中叫做lambda表达式[10]。一个lambda表达式有如下的形式:

[capture] (parameters) mutable exception attribute -> return_type { body } 

必须用方括号括起来的capture列表来开始一个lambda表达式的定义。

lambda函数的形参表比普通函数的形参表多了3条限制:

  1. 参数不能有缺省值
  2. 不能有可变长参数列表
  3. 不能有无名参数

如果lambda函数没有形参且没有mutable、exception或attribute声明,那么参数的空圆括号可以省略。但如果需要给出mutable、exception或attribute声明,那么参数即使为空,圆括号也不能省略。

如果函数体只有一个return语句,或者返回值类型为void,那么返回值类型声明可以被省略:

[capture](parameters){body} 

一个lambda函数的例子如下:

[](int x, int y) { return x + y; } // 從return語句中隱式獲得的返回值類型 
[](int& x) { ++x; } // 沒有return語句 -> lambda函數的返回值為void 
[]() { ++global_x; } // 沒有參數,僅僅是訪問一個全局變量 
[]{ ++global_x; } // 與前者相同,()可以被省略 

这个无名函数的返回值是decltype(x+y) (在上面的第一个例子中)。如果lambda函数体的形式是return expression,或者什么也每返回,或者所有返回语句decltype都能检测到同一类型,那么返回值类型可以被省略。

返回值类型可以显式指定,如下所示:

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

在这个例子中,一个临时变量,z,被创建来存储中间过程。与一般的函数一样,中间值在调用的前后并不存在。什么也没有返回的lambda表达式无需显式指定返回值,没有必要写-> void代码。

lambda函数可以捕获lambda函数外的具有automatic storage duration的变量。函数体与这些变量的集合合起来称做闭包

这些外部变量在声明lambda表达式时列在在方括号[和]中。空的方括号表示没有外界变量被capture。
这些变量被传值捕获或者引用捕获。对于传值捕获的变量,默认为只读。

修改这些变量将导致编译报错。但在lambda表达式的参数表的圆括号后面使用mutable关键字,就允许lambda函数体内的语句修改传值引用的变量,这些修改与lambda表达式(实际上是用函数对象实现)有相同的生命期,但不影响被传值捕获的外部变量的值。

lambda函数可以直接使用具有static存储期的变量。
如果在lambda函数的捕获列表中给出了static存储期的变量,编译时会给出警告,仍然按照lambda函数直接使用这些外部变量来处理。因此具有static存储期的变量即使被声明为传值捕获,修改该变量实际上直接修改了这些外部变量。编译器生成lambda函数对应的函数对象时,不会用函数对象的数据成员来保持被“捕获”的static存储期的变量。
示例:

[] // 沒有定義任何變量,但必须列出空的方括号。在Lambda表達式中嘗試使用任何外部變量都會導致編譯錯誤。 [x, &y] // x是按值傳遞,y是按引用傳遞 [&] // 任何被使用到的外部變量都按引用傳入。 [=] // 任何被使用到的外部變量都按值傳入。 [&, x] // x按值傳入。其它變量按引用傳入。 [=, &z] // z按引用傳入。其它變量按值傳入。 

下面这个例子展示了lambda表达式的使用:

std::vector<int> some_list{ 1, 2, 3, 4, 5 }; 
int total = 0; 
std::for_each(begin(some_list), end(some_list), [&total](int x) { total += x; } ); 


在类的非静态成员函数中定义的lambda表达式可以显式或隐式捕捉this指针,从而可以引用所在类对象的数据成员与函数成员。

lambda函数的函数体中,可以访问下述变量:

  • 函数参数
  • 局部声明的变量
  • 类数据成员(当函数声明在类中)
  • 具有静态存储期的变量(如全局变量)
  • 被捕获的外部变量
    • 显式捕获的变量
    • 隐式捕获的变量,使用默认捕获模式(传值或引用)来访问。

lambda函数的数据类型是函数对象,保存时必须用std::function模板类型或auto关键字,或用auto关键字。例如:

#include 
 #include  
#include  
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) << std::endl; 
 for(int i=0;i<3;i++) 
      std::cout << fa[i](2.0) << std::endl; 
 for(auto &f : fv) 
      std::cout << f(2.0) << std::endl; 
 for(auto &f : fa) 
      std::cout << f(2.0) << std::endl; 


   std::cout << eval(f0) << std::endl; std::cout << eval(f1) << std::endl; 
   std::cout << eval([](double x){return x*x;}) << std::endl; 
 return 0; 
} 

一个lambda函数的捕捉表达式为空,则可以用普通函数指针存储或调用。例如:

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

匿名函数

阅读(1044) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~