C++ primer 10.3 定制操作(lambda表达式和bind参数绑定)

本节总的思路:

1.需要定制操作的时候可以给算法传入函数

2.参数个数不匹配的时候,可以用lambda表达式

3.很多重复的操作不适合用lambda表达式可以用bind

向算法传递函数

sort函数不有一个重载版本,它的第3个参数是一个谓词

bool isShorter(const string &s1, const string &s2)  

{  

    return s1.size() < s2,size();  

}  

sort(v.begin(), v.end(), isShorter);

谓词是一个可调用的表达式,其返回结果是一个能用作条件的值。

接受谓词参数的算法对输入序列中的元素调用谓词。因此,元素类型必须能转换为谓词的参数类型。

谓词分为两类:

  • 一元谓词(只接受一个参数)

  • 二元谓词

 关于谓词:参考这篇 https://blog.youkuaiyun.com/caroline_wendy/article/details/15378055

除sort外,还有stable_sort, 称为稳定排序算法(维持相等元素的原有顺序)

stable_sort(v.begin(), v.end(), isShorter);

假如v中的元素是按字典顺序排好的,我们在调用stable_sort按长度排序时,等长元素的顺序不会变

如果想要查找序列中大于给定长度的元素,

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

{  

    return s.size() >= sz;  

}

遗憾的是查找算法find_if只接受一元谓词

这就需要用到lambda表达式了

lambda表达式

它是一个可调用对象

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

可以省略参数列表和返回类型

auto f = []{ return 42; };  

cout << f() << endl;

理解时,可以把lambda表达式理解为未命名的内联函数

lambda表达式可以捕获和它属于同一个局部作用域的变量

[sz](const string &a) { return a.size() >= sz; };

这样它就成了一个一元谓词,可以被find_if调用

string::size_type sz = 10;  

find_if(v.begin(), v.end(), [sz](const string &a){ return a.size() >= sz; } );

lambda可以直接使用定义在当前函数之外的名字

for_each(v.begin(), v.end(), [](const string &s){ cout << s << " "; });

cout就是定义在函数体之外的名字

lambda捕获

  • 值捕获

  • 引用捕获

  • 隐匿捕获

  • 混合捕获

值捕获

  1. 与参数的值传递类似

  2. 区别在于:拷贝过程发生在lambda创建时,而不是调用时

引用捕获

  1. 与参数的引用传递类似

  2. 同样要保证lambda调用时捕获的引用指向的局部变量还在

尽量减少捕获的数据,来避免潜在的问题。如果可能的话,避免捕获指针或引用

隐式捕获

不指明捕获哪个局部变量,而是让编译器自己去推断。

为了指示编译器需要推断捕获列表,需要在捕获列表中写一个&或=

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

隐式捕获sz,且是一种值捕获

混合捕获

如果混合使用显式捕获和隐式捕获

  1. 必须把隐式捕获放在前面

  2. 显式和隐式必须采用不同的捕获方式

可变lambda

通常值传递的时候,我们是无法通过形参改变实参的值的。在lambda中,可以使用mutable关键字做到

[v1]() mutable { return ++v1; };

返回类型

如果lambda体中包含除return语句之外的任何语句,则编译器假定此lambda返回void

[](int i){ if(i<0) return -i; else return i;};

该lambda包括return之外的语句(if),又没有指明返回类型,因此编译器推断它返回void,但实际函数体中返回的却是int,因此报错

此时应当指明返回类型。

[](int i) -> int { if(i<0) return -i; else return i;};

lambda使用尾置返回类型

参数绑定

如前面提到的,如果我们需要在很多地方使用相同的操作,定义一个函数比多次编写lambda表达式要好的多;类似的,如果lambda体包含多条语句,使用函数会比较清晰。

但是遇到只接受一元谓词的情况我们又不能使用函数来完成任务,因此标准库提供了bind函数。

它定义在头文件functional中。接受一个可调用对象,输出一个新的可调用对象,以便能适配参数列表。

auto newCallable = bind(callable, arg_list);
  • arg_list中callable的参数列表

  • arg_list中可以使用占位符_n

  • _1表示使用newCallable的第一个参数,以次类推

find_if(v.begin(), v.end(), bind(check_size, _1, sz));

点位符定义在命名空间std::placeholders中,通常我们一次只引用一个标识符,如using std::placeholders::_1,using std::placeholders::_2,我们也可以直接将整个命名空间都引入进来

using std::placeholders;

我们可以通过修改bind参数的顺序来调整函数的功能

sort(v.begin(), v.end(), isShorter);  

sort(v.begin(), v.end(), bind(isShorter, _2, _1));

运行的结果截然相反,一个是从小到大排列,一个是从大到小排列

绑定引用参数

bind的那些非点位符参数都是通过拷贝的形式传递的,如果需要使用引用的形式传递,可以使用ref关键字

for_each(v.begin(), v.end(), bind(print, ref(os), _1, ' '));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值