c++primer——第十章泛型算法lambda

一、谓词

谓词是一个调用表达式,其返回结果是一个能用做条件的值。
标准库算法为此分为两类:
1、一元谓词(意味着只能接受单一参数)
2、二元谓词(意味着他们有两个参数)
接受谓词的算法对输入序列中的元素调用谓词。因此元素类型必须能转换为谓词的参数类型。
以sort和isShorter举例
eg:

bool isShorter(const string &s1, const string &s2){
     return s1.size() < s2.size();
}
sort(word.begin(), words.end(), isShorter);

二、lambda表达式

问题背景:根据算法接受一元谓词还是二元谓词,我们传递给算法的为此必须严格接受一个或两个参数,当我们想用find_if判断一个string长度是否大于一个给定长度,我们需要传入一个string::size_type sz的参数,以及string,但是find_if只接受一元谓词,为了解决这个问题,我们引入lambda表达式。

介绍lambda:一个lambda表达式表示一个可调用的代码单元。我们可以理解为一个未命名的内联函数。其包括一个返回类型,一个参数列表和一个函数体。

[capture list](parameter list) -> return type{function body} //基本形式
//可以忽略参数列表和返回类型,但当我们想制定返回类型时,必须使用该形式中所用的尾置返回
1、捕获列表:

使用函数中定义的局部变量,且必须是那些明确指定的变量。
捕获方式分为三种:值捕获、引用捕获、隐式捕获。
首先是值捕获
采用值捕获的前提是变量可以拷贝。与参数不同,被捕获的变量的值实在lambda创建时拷贝,而不是调用时拷贝:

void fcn1(){
     size_t v1 = 42; //局部变量
     //将v1拷贝到名为f的可调用对象
     auto f = [v1]{return v1;};
     v1 = 0;
     auto j = f(); //j为42,f保存了我们创建时的拷贝
}

接着介绍引用捕获
一个以引用方式捕获的变量与其他任何引用行为类似。这里只谈问题和限制。如果我们引用捕获了一个变量,就必须确保被引用的对象在lambda执行的时候是存在的。
由于lambda捕获的都是局部变量,加入lambda在函数结束后执行,则其捕获的变量已经不存在了。
引用捕获有时候是必要的,比如在接受一个流参数时候,因为流无法拷贝,只能采取引用的策略。

void fcn2(){
     size_t v1 = 42; //局部变量
     auto f = [&v1]{return v1;};
     v1 = 0;
     auto j = f(); //j为0,f保存的是引用而不是拷贝
}

使用注意:
尽量保持lambda的变量捕获简单化。确保lambda每次执行的时候这些信息都有与其意义是程序员的责任。
一般来说我们应该尽量减少捕获的数据量,来避免捕获导致的潜在的问题。同时,如果可能的话我们应该尽量避免捕获指针和引用。

最后介绍隐式捕获
除了显示的列出我们希望使用的变量外,我们还可以让编译器根据lambda体中的代码来推断我们要使用的哪些变量。为了指示编译器推断捕获列表,应在捕获列表中写一个&或=。&告诉编译器采用引用捕获的方式,=表示采用值捕获方式。

wc = find_if(words.begin(), words.end(), [=](const string &s){return s.szie() >= sz;}); 

也可以混用隐式捕获和显示捕获,当我们混用时候捕获列表的第一个元素必须是一个&或=,同时第二个元素必须与第一个元素类型不同。

for_each(words.begin(), words.end(), [=,&os](const string &s){os << s << c;});
2、可变lambda

默认情况下对于一个值拷贝的变量lambda不改变其值,但当我们想改变时候需要在参数列表首加上关键字mutable。因此可便lambda能省略参数列表。

void fcn3(){
     size_t v1 = 42; //局部变量
     //f可以改变所捕获变量的值
     auto f = [v1] () mutable {return ++v1;};
     v1 = 0;
     auto j = f(); //j为43
}

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

void fcn4(){
     size_t v1 = 42; //局部变量
     //v1为非const变量引用
     auto f = [&v1]{return ++v1;};
     v1 = 0;
     auto j = f(); //j为1
}
3、指定lambda返回类型

默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。
因此当我们写如下语句时会报错

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

所以此时我们需要使用尾置返回类型

transform(vi.begin(), vi.end(), vi.begin(), [](int i) ->int {if (i < 0) return -i;else return i; });
4、向lambda传递参数

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

//isShorter
[](const string &a, const string &b){return a.size() < b.size();}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值