走进C++11(二十二) 之lambda(匿名函数)

本文围绕C++11中的Lambda函数展开。先介绍了Lambda函数与闭包的关系,闭包是引用自由变量的函数,Lambda可通过捕获实现闭包。接着阐述了Lambda函数的用处,如构造匿名函数,还说明了其规范。然后讲解了变量截取方式,包括值捕获、引用捕获和隐式捕获。最后提到Lambda函数对STL使用的便利。

图片

 

提示这篇文章可能较长,分为如下几部分

  • 什么是lambda函数

  • Lambda函数的用处

  • Lambda函数中的变量截取

  • Lambda函数和STL

 

1. 什么是lambda函数

 

lambda表达式无疑是C++11最激动人心的特性之一!它会使你编写的代码变得更优雅、更快速! 它实现了C++11对于支持闭包的支持。首先我们先看一下什么叫做闭包

 

维基百科上,对于闭包的解释是:

 

In programming languages, a closure (also lexical closure orfunction closure) is afunction or reference to a function together with a referencing environment—a table storing areference to each of the non-local variables (also called free variables or upvalues) of that function.[1] A closure—unlike a plainfunction pointer—allows a function to access those non-local variables even when invoked outside its immediatelexical scope.

The concept of closures was developed in the 1960s and was first fully implemented in 1975[citation needed] as a language feature in the Scheme programming language to support lexically scoped first-class functions. The use of closures is associated with functional programming languages such as Lisp and ML, as traditional imperative languages such as Algol, C and Pascal do not support returning nested functions as results from higher-order functions and thus do not require supporting closures either. Many moderngarbage-collected imperative languages support closures, such as Smalltalk (the first object-oriented language to do so)[2],JavaScript and C#.

 

简单来说,闭包(Closure)是词法闭包(Lexical Closure)或者函数闭包的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

 

在程序设计语言中,变量可以分为自由变量(free variable)与约束变量(bound variable)两种。简单来说,一个函数里局部变量和参数都被认为是约束变量;而不是约束变量的则是自由变量。

 

百度百科的解释可能更加通俗易懂:

 

闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。“闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。在 Scala、Scheme、Common Lisp、Smalltalk、Groovy、JavaScript、Ruby、 Python和Lua,objective c 等语言中都能找到对闭包不同程度的支持。

 

在编程领域我们可以通俗的说:子函数可以使用父函数中的局部变量,这种行为就叫做闭包!

 

有人说lambda是C++的语法糖。在C++11中labmda可以通过捕获实现闭包。

 

2. Lambda函数的用处

 

lambda 表达式可以方便地构造匿名函数,如果你的代码里面存在大量的小函数,而这些函数一般只被调用一次,那么不妨将他们重构成 lambda 表达式。

 

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

 

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

 

其中

 

  • (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 指针。

  • [&] 以引用的方式捕获所有的外部自动变量。

  • [=] 以值的方式捕获所有的外部自动变量。

  • [] 不捕获外部的任何变量。

     

此外,params 指定 lambda 表达式的参数。

 

3. Lambda函数中的变量截取

 

捕获:

 

(1)值捕获

 

与参数传值类似,值捕获的前提是变量可以拷贝,不同之处则在于,被捕获的变量在lambda 表达式被创建时拷贝,而非调用时才拷贝

 

void f(const int*);void g(){    const int N = 10;    [=]{         int arr[N]; // not an odr-use: refers to g's const int N        f(&N); // odr-use: causes N to be captured (by copy)               // &N is the address of the closure object's member N, not g's N    }();}

 

(2)引用捕获

 

与引用传参类似,引用捕获保存的是引用,值会发生变化。

 

#include <iostream> auto make_function(int& x) {  return [&]{ std::cout << x << '\n'; };} int main() {  int i = 3;  auto f = make_function(i); // the use of x in f binds directly to i  i = 5;  f(); // OK; prints 5}

 

(3)隐式捕获

 

[] 空捕获列表

[name1, name2, ...] 捕获一系列变量

[&] 引用捕获, 让编译器自行推导捕获列表

[=] 值捕获, 让编译器执行推导应用列表

 

void f3() {    float x, &r = x;    [=]    { // x and r are not captured (appearance in a decltype operand is not an odr-use)        decltype(x) y1; // y1 has type float        decltype((x)) y2 = y1; // y2 has type float const& because this lambda                               // is not mutable and x is an lvalue        decltype(r) r1 = y1;   // r1 has type float& (transformation not considered)        decltype((r)) r2 = y2; // r2 has type float const&    };}

 

当然C++14,C++17,C++20对lambda做了更多的更新,这里就不多讲了。

 

4. Lambda函数和STL 

 

lambda函数的引入为STL的使用提供了极大的方便。比如下面这个例子,当你想便利一个vector的时候,原来你得这么写:

 

vector<int> v;  v.push_back( 1 );  v.push_back( 2 );  //...  for ( auto itr = v.begin(), end = v.end(); itr != end; itr++ )  {      cout << *itr;  } 

 

现在有了lambda函数你就可以这么写:

 

    vector<int> v;      v.push_back( 1 );      v.push_back( 2 );      //...      for_each( v.begin(), v.end(), [] (int val)      {          cout << val;      } );  

 

而且这么写了之后执行效率反而提高了。因为编译器有可能使用”循环展开“来加速执行过程。

 

 

 

引用:

https://blog.youkuaiyun.com/anzhsoft/article/details/17414665

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值