一、简介

我们可以向一个算法传递任何类别的可调用对象。对于一个对象或一个表达式,如果可以对其使用调用运算符,则称它为可调用的。即,如果e是一个可调用的表达式,则我们可以编写代码e(args),其中args是一个逗号分隔的一个或多个参数的列表。

可调用对象分别有:1、函数和函数指针;2、重载了函数调用运算符的类;3、lambda表达式。

一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。但与函数不同,lambda可能定义在函数内部。一个lambda表达式具有如下形式:

[capture list](parameter list) -> return type {functiom body }

其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空);return type、parameter list和function body与任何普通函数一样,分别表示返回类型、参数列表和函数体。但是,与普通函数不同,lambda必须使用尾置返回来指定返回类型。

我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体

auto f = [] { return ; };

此例中,我们定义了一个可调用对象f,它不接受参数,返回42.

Lambda的调用方式与普通函数的调用方式相同,都是使用调用运算符:

cout << f() << endl;  // 打印42

在lambda中忽略括号和参数列表等价于指定一个空参数列表。在此例中,当调用f时,参数列表是空的。如果忽略返回类型,lambda根据函数体中的代码推断出返回类型。如果函数体只是一个return语句,则返回类型从返回的表达式的类型推断而来。否则,返回类型为void。

二、向lambda传递参数

与普通函数不同,lambda不能有默认参数。因此,一个lambda调用的实参数目永远与形参数目相等。

例如:

[](const string &a, const string &b)

  { return a.size() < b.size(); }

空捕获列表表明此lambda不使用它所在函数中的任何局部变量。如下所示,可以使用此lambda来调用stable_sort:

// 按长度排序,长度相同的单词维持字典序

Stable_sort(words.begin(), words.end(),

           [](const string &a, const string &b)

            { return a.size() < b.size();});

当stable_sort需要比较两个元素时,它就会调用给定的这个表达式。

三、使用捕获列表

接下来我们需要编写一个可传递给find_if的可调用表达式。我们希望这个表达式能将输入序列中每个string的长度与biggies函数中的sz参数的值进行比较。

在本例中,我们的lambda会捕获sz,并只有单一的string参数。其函数体会将string的大小与捕获的sz的值进行比较:

[sz](const string &a)

   { return a.size() >= sz; };

PS:捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。

调用范例:

四、lambda捕获和返回

当定义一个lambda时,编译器生成一个与lambda对应的新的(未命名的)类类型。

值捕获

类似参数传递,变量的捕获方式也可以是值或引用。当lambda采用值捕获的时候,前提条件为变量可以拷贝,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝:

void fcn1()

{

    size_t v1 = ; // 局部变量

    // 将v1拷贝到名为f的可调用对象

    auto f = [v1] { return v1; };

    v1 = ;

    auto j = f();  //j为42,f保存了我们创建它时v1的拷贝

}

引用捕获

我们定义lambda时可以采用引用方式捕获变量。例如:

void fcn2()

{

    size_t v1 = ;

    // 对象f2包含v1的引用

    auto f2 = [&v1] { return v1; };

    v1 = ;

    auto j = f2(); // j为0,f2保存v1的引用,而非拷贝

}

隐式捕获

除了显示列出我们希望使用的来自所在函数的变量之外,还可以让编译器根据lambda体重的代码来推断我们要使用哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用捕获引用方式,=则表示采用值捕获方式。例如:

// sz为隐式捕获,值捕获方式

Wc = find_if(words.begin(), words.end(),

          [=](const string &s)

            { return s.size() >= sz; });

如果我们希望对一部分变量采用值捕获,对其他变量采用引用捕获,可以混合使用隐式捕获和显式捕获:

 void biggies(vertor<string> &words,

           vertor<string>::size_type sz,

           ostream &os = cout, char c = ‘ ‘)

 {

     //os 隐式捕获,引用捕获方式:c显式捕获,值捕获方式

     for_each(words.begin(), words.end(),

             [&, c](const string &s) { os << s << c; });

     //os显式捕获,引用捕获方式;c隐式捕获,值捕获方式

     For_each(words.begin(), words.end(),

             [=, &os](const string &s) { os << s << c; });

 }

当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=.

可变lambda

默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable。

 void fcn3()

 {

     size_t v1 = ;

     auto f = [v1] () mutable { return ++ v1; };

     v1 = ;

     auto j = f(); // j为42

 }

指定lambda返回类型

当我们需要为一个lambda定义返回类型时,必须使用尾置返回类型:

 Transform(vi.begin(), vi.end(), vi.begin(),

          [](int i) - > int

  { if (i < ) return –i; else return i; });

五、参数绑定

对于那种只在一两个地方使用的简单操作,lambda表达式是最有用的。如果我们需要在很多地方使用相同的操作,通常应该定义一个函数,而不是多次编写相同的lambda表达式。

但是,对于捕获局部变量的lambda,用函数来替代它就不是那么容易了。例如我们用在find_if调用中的lambda比较一个string和一个给定大小。我们可以很容易地编写一个完成同样工作的函数:

 bool check_size(const string &s, string::size_type sz)

 {

     return s.size() >= sz;

 }

标准库bind函数

我们可以额解决向check_size传递一个长度参数的问题,方法是使用一个新的名为bind的标准库函数,它定义在头文件functional中。可以将bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式为:

auto newCallable = bind(callable, arg_list);

其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。即,当我们调用newCallable时,newCallable会调用callable,并传递给它arg_list中的参数。

绑定chenck_size的sz参数

作为一个简单的例子,我们将使用bind生成一个调用check_size的对象,如下所示,它用一个定值作为其大小参数来调用check_size:

 // chenck6是一个可调用对象,接受一个string类型的参数

 // 并用此string和值6来调用check_size

 auto check6 = bind(check_size, _1, );

调用check6必须传递给它一个string类型的参数,check6会将此参数传递给check_size。

string s = “hello”;

bool b1 = check6(s); //check6(s)会调用check_size(s,6)

使用bind,我们可以将原来基于lambda的find_if调用:

auto wc = find_if(words.begin(), words.end(),

              [sz](const string &a)

替换为如下使用check_size的版本:

auto wc = find_if(words.begin(), words.end(), bind(check_size, _1, sz));

参考:《C++ Primer》

STL之lambda表达式的更多相关文章

  1. STL - C++ 11的Lambda表达式(下)

    关于lambda的基础知识,请参考上一篇的地址如下: http://www.cnblogs.com/davidgu/p/4825625.html 我们再举个STL使用Lambda来进行排序的例子,如下 ...

  2. 「C++11」Lambda 表达式

    维基百科上面对于 lambda 的引入是如下描述的: 在标准 C++,特别是当使用 C++ 标准程序库算法函数诸如 sort 和 find.用户经常希望能够在算法函数调用的附近定义一个临时的述部函数( ...

  3. C++11中的Lambda表达式

    原文地址:C++中的Lambda表达式 作者:果冻想 一直都在提醒自己,我是搞C++的:但是当C++11出来这么长时间了,我却没有跟着队伍走,发现很对不起自己的身份,也还好,发现自己也有段时间没有写C ...

  4. Cocos2d-x v3.0 新的事件调度方法 lambda表达式的使用

    欢迎添� Cocos2d-x 交流群: 193411763 转载请注明原文出处:http://blog.csdn.net/u012945598/article/details/24603251 Coc ...

  5. Qt5中使用lambda表达式

    c11新特性中加入了lambda表达式,所以Qt 也支持 需在.pro文件中加入 CONFIG += c++11 例子: QString program = "C:/Windows/Syst ...

  6. (转载)C++lambda表达式

    (转载)http://www.cnblogs.com/L-hq815/archive/2012/08/23/2653002.html lambda表达式 C++ 语言中的lambda表达式在很多情况下 ...

  7. Lambda 表达式的示例-来源(MSDN)

    本文演示如何在你的程序中使用 lambda 表达式. 有关 lambda 表达式的概述,请参阅 C++ 中的 Lambda 表达式. 有关 lambda 表达式结构的详细信息,请参阅 Lambda 表 ...

  8. C++11 lambda表达式学习

    lambda表达式是函数式编程的基础.咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下.这里只是介绍C++11中的lambda表达式自己的认识.这里有参考文档h ...

  9. C++11里面的Lambda表达式

    Lambda Expressions in C++ C++中的Lambda表达式 In Visual C++, a lambda expression—referred to as a lambda— ...

随机推荐

  1. max_binlog_cache_size

    http://blog.mimvp.com/2017/07/mysql-yi-ge-can-shu-yin-qi-de-dang-ji-xue-an/ max_binlog_cache_size 表示 ...

  2. 安装oracle 11gr2 rac on solaris

    http://candon123.blog.51cto.com/704299/389470

  3. MYSQL 源代码编绎脚本

    http://blog.csdn.net/hopingwhite/article/details/5808101

  4. Access-Control-Allow-Origin,跨域

    1.浏览器的同源安全策略 浏览器只允许请求当前域的资源,而对其他域的资源表示不信任.那怎么才算跨域呢? 请求协议http,https的不同 域domain的不同 端口port的不同 好好好,大概就是这 ...

  5. DI容器Ninject在管理接口和实现、基类和派生类并实现依赖注入方面的实例

    当一个类依赖于另一个具体类的时候,这样很容易形成两者间的"强耦合"关系.我们通常根据具体类抽象出一个接口,然后让类来依赖这个接口,这样就形成了"松耦合"关系,有 ...

  6. mormot当作内存数据库(缓存)使用

    mormot当作内存数据库(缓存)使用 mormot的TSQLRestStorageInMemory可以作为内存数据库来使用. 上图是在笔者4代I5笔记本上做的测试,增加10万记录,耗时:562毫秒. ...

  7. @selector 如何调用在另一个类中的静态函数?

    可以在同一个类的methodName这个函数中再调用另一个类中的静态方法

  8. 实现Hadoop的Writable接口Implementing Writable interface of Hadoop

    As we saw in the previous posts, Hadoop makes an heavy use of network transmissions for executing it ...

  9. TRIZ理论的进化法则分析(TRIZ学习笔记)

    人们在创新和完好系统的过程能够遵循一定的规律(或者叫法则).从而降低创新和完好系统过程中的试错成本,以下就TRIZ的八大进化原则来进行说明(这个八大法则是前人们的总结,我这里当然会增加我的理解). 我 ...

  10. 第十六章 springboot + OKhttp + String.format

    模拟浏览器向服务器发送请求四种方式: jdk原生的Http包下的一些类 httpclient(比较原始,不怎么用了):第一章 HttpClient的使用 Okhttp(好用,推荐) retrofit( ...