lambda表达式

根据算法接受一元谓词还是二元谓词,我们传递给算法的谓词必须严格接受一个或两个参数。但是,有时我们希望进行的操作需要更多参数,超出了算法对谓词的限制。这就需要用到lambda表达式。

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

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

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

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

auto f = []{return 42;}
cout << f() << endl;

在lambda中忽略括号和参数列表等价于指定一个空参数列表。如果忽略返回类型,lambda根据函数体中的代码推断出返回类型。如果函数体只是一个return语句,则返回类型从返回的表达式的类型推断而来。如果lambda的函数体包含任何单一return语句之外的内容,且未指定返回类型,则返回void.
与一个普通函数调用类似,调用一个lambda时给定的实参被用来初始化lambda的形参。通常,实参和形参的类型必须匹配。但与普通函数不同,lambda不能有默认参数。因此,一个lambda调用的实参数目永远与形参数目相等。一旦形参初始化完毕,就可以执行函数体了。下面是一个与isShorter函数完成相同功能的lambda:

[](const string &a, const string &b) {return a.size() < b.size(); }
stable_sort(words.begin(), words.end(),
            [](const string &a, const string &b) {return a.size() < b.size();});

一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量。捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。

其实,当定义一个lambda时,编译器生成一个与lambda相对应的未命名类的未命名对象,其实这是一个函数对象。捕获列表中获得的局部变量成为了这个函数对象的数据成呀un。默认情况下,从lambda生成的类都包含一个对应该lambda所捕获的变量的数据成员,类似任何类的数据成员,lambda的数据成员也在lambda对象创建时被初始化。

类似参数传递,变量的捕获方式也可以是值或引用。首先来说值捕获,与传值参数类似,采用值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值是在lambda创建时拷贝,而不是调用时拷贝。随后对其修改不会影响到lambda内对应的值。接着来说引用捕获,引用捕获与引用返回有着相同的问题和限制,如果我们采用引用方式捕获一个变量,就必须确保被引用的对象在lambda执行的时候是存在的。lambda捕获的是局部变量,这些变量在函数结束后就不复存在了。如果lambda可能在函数结束后执行,捕获的引用指向的局部变量已经消失。所以建议是尽量保持lambda的变量捕获简单化。

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

[&, c]... [=, &os]...

当我们混合使用隐式捕获和显式捕获时,捕获列表中的第一个元素必须是一个&或=。此符号指定了默认捕获方式为引用或值。当混合使用隐式捕获和显式捕获时,显式捕获的变量必须使用与隐式捕获不同的方式。
关于可变lambda,默认情况下,对于一个值被拷贝的变量,lambda不会改变其值。如果我们希望能改变一个被捕获的变量的值,就必须在参数列表后面加上关键字mutable。因此,可变lambda不能省略参数列表:

void fcn3()
{
    size_t v1 = 42; // local variable
    // f can change the value of the variables it captures.
    auto f = [v1] () mutable {return ++v1;}
    v1 = 0;
    auto j = f(); // j is 43
}

上例中如果不加() mutable就会出现错误信息error: increment of read-only variable ‘v1’

一个引用捕获变量是否可以修改依赖于此引用指向的是一个const类型还是一个非const类型:

void fcn4()
{
    size_t v1 = 42; // local variable
    // v1 is a reference to a nonconst variable
    // we can change that variable through the reference inside f2
    auto f2 = [&v1] () {return ++v1;}
    v1 = 0;
    auto j = f2(); // j is 1
}

关于lambda表达式的返回类型,先看一个例子:

transform(vi.begin(), vi.end(), vi.begin(), [](int i) {return i < 0 ? -i : i; });

只有一个return语句,返回一个条件表达式的结果。我们无须指定返回类型,因为可以根据条件运算符的类型推断出来。但是,如果我们将程序改写为看起来是等价的if语句,就会产生编译错误:

//错误:不能推断lambda的返回类型
transform(vi.begin(), vi.end(), vi.begin(), [](int i){ if(i < 0) return -i; else return i;});

编译器推断这个版本的lambda返回类型为void,但它返回了一个int值。

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

transform(vi.begin(), vi.end(), vi.begin(),
          [](int i) -> int { if(i < 0) return -i; else return i;});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容