开发者社区> anzhsoft> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

C++闭包: Lambda Functions in C++11

简介:
+关注继续查看

a 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 等语言中都能找到对闭包不同程度的支持。
在编程领域我们可以通俗的说:子函数可以使用父函数中的局部变量,这种行为就叫做闭包!

还是上code吧:还是从Hello, World!开始

auto func = [] { cout << "Hello, World!" << endl; } ();

func();// 调用函数输出Hello, World!
我们可以像创建变量一样方便的创建函数!使用STL我们会为了某个很简单的实现不得不实现一个一个的小函数,a lambda的引入可以让我们写出更紧凑的代码:
bool compare(int i,int j) { return (i>j); }
vector<int> v;
// using default comparison (operator <):
std::sort (v.begin(), v.end());  // using function as comp
std::sort (v.begin(), v.end(), compare); 

// using lambda
std::sort (v.begin(), v.end(), [](int i, int j){ return i > j; });

// Print the contents of the vector.
std::for_each(v.begin(), v.end(), [](int i) { cout << i << " "; });

// C++11 style: using begin(v) instead of v.begin()

std::for_each(begin(v), end(v), [](int i) { cout << i << " "; });

仅仅通过sort和对于vector的遍历,我们就可以看到a lambda的巨大威力。当然了,对于C++11,我们可以使用“现代风格”进行数组的遍历:
for( auto d : myvector ) {
  cout<< d <<" ";
}
另外一个快速示例:找到v里面大于x并且小于y的第一个元素。在C++11中,最简单和干净的代码就是调用一个标准函数。
// C++98/03: 直接编写一个循环 (使用std::find_if会非常困难)
vector<int>::iterator i = v.begin();
for( ; i != v.end(); ++i ) {
if( *i > x && *i < y ) break;
}
 
// C++11: use std::find_if
auto i = find_if( begin(v), end(v), [=](int i) { return i > x && i < y; } );
数组求和:
for_each(begin(v), end(v), [&](int n){ sum += n;)}
最后一个使得代码高效优雅的例子:
// The number of elements in the vector.
const int elementCount = 9;
// Create a vector object with each element set to 1.
vector<int> v(elementCount, 1);

// These variables hold the previous two elements of the vector.
int x = 1;
int y = 1;

// Assign each element in the vector to the sum of the previous two elements.
generate_n(begin(v) + 2, elementCount - 2, [=]() mutable throw() -> int {      
      // Generate current value.
      int n = x + y;
      // Update previous two values.
      x = y;
      y = n;
      return n;
});
下面对a lambda表达式语法进行详细的阐述:

Structural elements of a lambda expression

1. lambda-introducer: 定义引用自由变量的方式。

    [] // 没有定义任何变量。使用未定义变量会导致错误。

    [x, &y] // x 以传值方式传入(默认),y 以引用方式传入。

    [&] // 任何被使用到的外部变量皆隐式地以引用方式加以使用。

    [=] // 任何被使用到的外部变量皆隐式地以传值方式加以使用。

    [&, x] // x 显示地以传值方式加以使用。其余变量以引用方式加以使用。

    [=, &z] // z 显示地以引用方式加以使用。其余变量以传值方式加以使用。

2. lambda-parameter-declaration-list: 参数列表。但是参数不可以有默认值,不可以使用变长参数,不可以有unamed arguments

3. mutable-specification :使得传值引入的变量可以修改。这个修改因为是修改的外部变量的拷贝,因此并不会影响它本来的值

4. exception-specification:throw()该函数不能抛出异常。如果抛出异常,编译器将报warning C4297。 throw(...) 可以抛出异常。throw(type)可以抛出type的异常

5. lambda-return-type-clause:如果仅有0/1个return的话可以省略。返回值可以是lambda表达式。

// compile with: /EHsc
#include <functional>
// The following code declares a lambda expression that returns 
// another lambda expression that adds two numbers. 
// The returned lambda expression captures parameter x by value.
auto g = [](int x) -> function<int (int)> { return [=](int y) { return x + y; }; };

// The following code declares a lambda expression that takes another
// lambda expression as its argument.
// The lambda expression applies the argument z to the function f and adds 1.
auto h = [](const function<int (int)>& f, int z) { return f(z) + 1; };

// Call the lambda expression that is bound to h. 
auto a = h(g(7), 8);

最后一个问题, what's the type of a lambda expression? 使用C++11增强的各类function的wrapper std::function, 下面是使用a lambda作为代理(Delegate)的例子,仅仅是例子,不要问为什么要这样用:)
#include <functional>

class EmailProcessor
{
public:
    void receiveMessage (const std::string& message)
    {
        if ( _handler_func ) 
        {
            _handler_func( message );
        }
        // other processing
    }
    void setHandlerFunc (std::function<void (const std::string&)> handler_func)
    {
        _handler_func = handler_func;
    }

private:
        std::function<void (const std::string&)> _handler_func;
};

//当收到Message时,我们希望有个回调函数能够处理

class MessageSizeStore
{
    MessageSizeStore () : _max_size( 0 ) {}
    void checkMessage (const std::string& message ) 
    {
        const int size = message.length();
        if ( size > _max_size )
        {
            _max_size = size;
        }
    }
    int getSize ()
    {
        return _max_size;
    }

private:
    int _max_size;
};

//我们希望每次Message到来时,统计出历史的最大长度(好无聊啊。。。)

EmailProcessor processor;
MessageSizeStore size_store;
processor.setHandlerFunc( 
        [&] (const std::string& message) { size_store.checkMessage( message ); } 
);

引用:

http://www.cprogramming.com/c++11/c++11-lambda-closures.html

http://rednaxelafx.iteye.com/blog/184199

http://www.cplusplus.com/reference/algorithm/sort/

Lambda Expression Syntax

http://msdn.microsoft.com/en-us/library/46h7chx6.aspx


http://msdn.microsoft.com/en-us/library/dd293599.aspx#higherOrder


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
C++的匿名函数(lambda表达式)
C++的匿名函数(lambda表达式)
0 0
C++的lambda是函数还是对象?
关于C++的lambda是函数还是对象,这其实不是一个一概而论的问题。
0 0
C++模拟面试:宏、lambda、智能指针闲谈
C++模拟面试:宏、lambda、智能指针闲谈
0 0
【云驻共创】当c++和Lambda表达式相遇,会碰撞出怎样的火花?
【云驻共创】当c++和Lambda表达式相遇,会碰撞出怎样的火花?
0 0
C++11 Lambda表达汇总总结
C++11 Lambda表达汇总总结
0 0
C++中的Lambda表达式
在 `C++11` 及之后的版本中,`C++` 提供了 `lambda` 表达式,它是一种方便了参数传递和定义匿名函数的方法。该方法通常用于封装算法、执行异步方法 ,也就是说比较适用于少量的代码。
0 0
C++雾中风景8:Lambda表达式
上一篇C++的博客是Long Long ago了,前文讲到在看Lambda表达式的内容。笔者首次接触Lambda表达式应该是学习Python语言的时候,当时也不太明白这种表达方式的精髓,后续接触了Scala与Java8的链式调用与Lambda结合的方式,深陷无法自拔。
950 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
C++课程-对象模型
立即下载
使用C++开发PHP7扩展
立即下载
C++对象模型
立即下载