谓词、内建函数对象、函数对象适配器
一、谓词概念
谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接收一个参数,那么叫做一元谓词,如果接收两个参数,那么叫做二元谓词,谓词可作为一个判断式。
二、内建函数对象
STL内建了一些函数对象,分为:算数类函数对象,关系运算类函数对象,逻辑运算类仿函数。这些仿函数所产生的对象,用法和一般函数完全相同,当然我们还可以产生无名的临时对象来履行函数功能,使用内建函数对象,需要引入头文件#include<functional>。
6个算数类函数对象,除了negate是一元运算,其他都是二元运算。
6个关系运算类函数对象,每一种都是二元运算。
逻辑运算类仿函数,not为一元运算,其余为二元运算。
案例(以plus为例):
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <functional> using namespace std; void test01() { //使用内建函数对象声明一个对象 plus<int> myplus; cout << myplus(10, 20) << endl;//30 //使用匿名临时对象 cout << plus<int>()(5, 6) << endl;//11 } int main(void) { test01(); return 0; }
三、函数对象适配器
函数对象适配器是完成一些配接工作,这些配接包括绑定(bind),否定(negate),以及一对一般函数或成员函数的修饰,使其成为函数对象,重点掌握函数对象适配器(红色字体):
bind1st:将参数绑定为函数对象的第一个参数
bind2nd:将参数绑定为函数对象的第二个参数
not1:对一元函数对象取反
not2:对二元函数对象取反
ptr_fun:将普通函数修饰成函数对象
mem_fun:修饰成员函数
mem_fun_ref:修饰成员函数
1、绑定适配器:bind1st、bind2nd
bind1st和bind2nd函数把一个二元函数对象绑定成为一个一元函数对象。但是由于二元函数对象接受两个参数,在绑定成为一元函数对象时需要将原来两个参数中的一个绑定下来。也即通过绑定二元函数对象的一个参数使之成为一元函数对象的。bind1st是绑定第一个参数,bind2nd则是绑定第二个参数。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; struct MyPrint:public binary_function<int,int,void> { void operator()(int v, int val) const { cout << v + val << " "; cout << "v:" << v << " val:" << val << endl; } }; //仿函数适配器 bind1st bind2nd 绑定适配器 void test01() { vector<int> v; for (int i = 0;i < 10;i++) { v.push_back(i); } int addNum = 200; //绑定适配器 将一个二元函数对象转变成一元函数对象 for_each(v.begin(), v.end(), bind1st(MyPrint(), addNum)); /* 200 v:200 val:0 201 v:200 val:1 202 v:200 val:2 203 v:200 val:3 204 v:200 val:4 205 v:200 val:5 206 v:200 val:6 207 v:200 val:7 208 v:200 val:8 209 v:200 val:9 */ for_each(v.begin(), v.end(), bind2nd(MyPrint(), addNum)); /* 200 v:0 val:200 201 v:1 val:200 202 v:2 val:200 203 v:3 val:200 204 v:4 val:200 205 v:5 val:200 206 v:6 val:200 207 v:7 val:200 208 v:8 val:200 209 v:9 val:200 */ //由此可得bind1st与bind2nd的区别: //bind1st 将addNum绑定为函数对象的第一个参数; //bind2nd 将addNum绑定为函数对象的第二个参数 } int main(void) { test01(); return 0; }
2、取反适配器:not1和not2
not1是构造一个与谓词结果相反的一元函数对象,not2是构造一个与谓词结果相反的二元函数对象。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; struct MyCompare :public binary_function<int, int, bool> { //由大到小比较 bool operator()(int v1,int v2) const { return v1 > v2; } }; struct MyPrint02 { void operator()(int v) { cout << v << " "; } }; struct MyGreater5:public unary_function<int,bool> { //第一个大于5的数 bool operator()(int v) const { return v > 5; } }; //仿函数适配器 not1 not2 取反适配器 void test02() { vector<int> v; for (int i = 9;i > -1;i--) { v.push_back(i); } for_each(v.begin(), v.end(), MyPrint02()); cout << endl;//9 8 7 6 5 4 3 2 1 0 sort(v.begin(), v.end(), not2(MyCompare()));//由小到大排序 for_each(v.begin(), v.end(), MyPrint02());//0 1 2 3 4 5 6 7 8 9 cout << endl; //not1 not2 区别 //如果对一元谓词取反,用not1; //如果对二元谓词取反,用not2; vector<int>::iterator it = find_if(v.begin(), v.end(), not1(MyGreater5()));//第一个小于等于5的值 if (it == v.end()) { cout << "没有找到!" << endl; } else { cout << *it << endl;//0 } } int main(void) { test02(); return 0; }
3、函数对象适配器:ptr_fun
ptr_fun是将一个普通的函数适配成一个仿函数(functor)
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; //仿函数适配器 ptr_fun void MyPrint03(int val1,int val2)//普通函数MyPrint03 { cout << val1 + val2 << " "; cout << "val1:" << val1 << " val2:" << val2 << endl; } void test03() { vector<int> v; for (int i = 0;i < 10;i++) { v.push_back(i); } //ptr_fun:把普通函数 转成 函数对象 for_each(v.begin(), v.end(), bind1st(ptr_fun(MyPrint03),10)); /* 10 val1:10 val2:0 11 val1:10 val2:1 12 val1:10 val2:2 13 val1:10 val2:3 14 val1:10 val2:4 15 val1:10 val2:5 16 val1:10 val2:6 17 val1:10 val2:7 18 val1:10 val2:8 19 val1:10 val2:9 */ } int main(void) { test03(); return 0; }
4、成员函数适配器:mem_fun、mem_fun_ref
mem_fun_ref的作用和用法跟mem_fun一样,唯一的不同就是:当容器中存放的是对象实体的时候用mem_fun_ref,当容器中存放的是对象的指针的时候用mem_fun。
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; void test04() { //如果容器中存放的对象或者对象指针,for_each算法打印的时候,调用类自己提供的打印函数 vector<Person> v; Person p1(10, 20), p2(30, 40), p3(50, 60); v.push_back(p1); v.push_back(p2); v.push_back(p3); //mem_fun_ref 格式:&类名::函数名 for_each(v.begin(), v.end(), mem_fun_ref(&Person::show)); /* age:10 id:20 age:30 id:40 age:50 id:60 */ cout << "-------------" << endl; vector<Person*> v1; v1.push_back(&p1); v1.push_back(&p2); v1.push_back(&p3); for_each(v1.begin(), v1.end(), mem_fun(&Person::show)); /* age:10 id:20 age:30 id:40 age:50 id:60 */ //mem_fun_ref mem_fun 区别? //如果存放的是对象指针 使用mem_fun //如果存放的是对象 使用mem_fun_ref } int main(void) { test04(); return 0; }