1、C++11中引入的bind绑定器和function函数对象
1.1、bind1st和bind2d使用
-
bind1st和bind2d是STL中的;
-
bind1st和bind2d作用: 将二元函数对象的一个参数绑定,使其变为一元函数对象;
-
缺点: 只能用于二元函数对象。
-
函数对象: 对象拥有小括号重载函数的对象。
#include <functional> --包含c++库中的所有的函数对象
#include <algorithm> --包含了c++库中的所有的泛型算法

例1—改变排序顺序


greater是一个二元函数对象:(因为一次需要从容器中拿2个函数对象出来)


传入greater函数对象后,就是从大到小进行排序了。

#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <ctime>
using namespace std;
template<typename Container>
void showContainer(Container& con)
{
typename Container::iterator it = con.begin();
//编译器是从上到下编译的,这个还没有实例化,它不知道这个名字作用域后面的iterator是类型还是变量
//typename告知编译器后面类型的作用域后面是类型
for (; it != con.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
vector<int> vec;
srand(time(nullptr));
for (int i = 0; i < 20; ++i)
{
vec.push_back(rand() % 100 + 1);//随机出来的数字,并不是有序的
}
showContainer(vec);
sort(vec.begin(), vec.end());//默认小到大排序,传入的是起始和末尾的后继的迭代器
showContainer(vec);
//greater 二元函数对象
sort(vec.begin(), vec.end(), greater<int>());//大到小排序
showContainer(vec);
return 0;
}
例2—将70按顺序插入到vec容器中
问题:将70按顺序插入到vec容器中
使用find_if函数需要传入一个一元函数对象(一次从容器中拿出一个元素和70进行比较 )
注意:库里面提供的函数对象都是二元的,没有办法直接使用;怎么办?
使用绑定器; 将二元函数对象转换为一元函数对象;


使用greater函数对象,用绑定器将其绑定为一元函数对象:
bind1st:绑定第1个参数,也就是70 > b,b就是容器中的每个元素,找到第一个小于70的位置。

使用less函数对象,用绑定器将其绑定为一元函数对象:
bind2d:绑定第2个参数,a < 70,找到第一个小于70的位置。
注意,这是生成的随机数

1.2、bind1st和bind2d底层原理
实现自己的find_if—my_find_if

函数模板,接收用户传入的二元函数对象和所要绑定的值;
为什么用函数模板去封装?
不用自己去写模板参数,可以自己推导。
mybind1st就是将函数对象封装了一下。


测试:

直接使用自己的即可。
bind2d就是在类中重载时,将绑定的位置交换即可。
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <ctime>
using namespace std;
template<typename Container>
void showContainer(Container& con)
{
typename Container::iterator it = con.begin();
for (; it != con.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
}
template<typename Iterator, typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare comp)
//遍历这2个迭代器之间的元素,如果满足函数对象的运算,就返回当前迭代器,如果都不满足,返回end()
{
for (; first != last; ++first)
{
if (comp(*first))//comp.operator()(*first)一元函数对象,因为要从容器拿1个元素和它指定的元素比较
//my_find_if需要1元函数对象,而在库里面都是二元的
{
return first;
}
}
return last;
}
template<typename Compare, typename T>
class _mybind1st//绑定器是函数对象的一个应用
{
public:
_mybind1st(Compare comp, T val)
:_comp(comp), _val(val)
{}
bool operator()(const T& second)
{
return _comp(_val, second);//greater
}
private:
Compare _comp;
T _val;
};
//mybind1st(greater<int>(), 70)
template<typename Compare, typename T>
_mybind1st<Compare, T> mybind1st(Compare comp, const T& val)
{
//直接使用函数模板,好处是,可以进行类型的推演
return _mybind1st<Compare, T>(comp, val);
}
int main()
{
vector<int> vec;
srand(time(nullptr));
for (int i = 0; i < 20; ++i)
{
vec.push_back(rand() % 100 + 1);
}
showContainer(vec);
sort(vec.begin(), vec.end());//默认小到大排序
showContainer(vec);
//greater 二元函数对象
sort(vec.begin(), vec.end(), greater<int>());//大到小排序
showContainer(vec);
/*
把70按顺序插入到vec容器当中 找第一个小于70的数字
operator()(const T &val)
greater a > b
less a < b
绑定器 + 二元函数对象 =》 一元函数对象
bind1st: + greater bool operator()(70, const _Ty& _Right)
bind2nd: + less bool operator()(const _Ty& _Left, 70)
*/
auto it1 = my_find_if(vec.begin(), vec.end(),
mybind1st(greater<int>(), 70));
//auto it1 = my_find_if(vec.begin(), vec.end(),bind2nd(less<int>(), 70));
if (it1 != vec.end())
{
vec.insert(it1, 70);
}
showContainer(vec);
return 0;
}
1.3、function函数对象类型的应用示例

function:函数对象类;
- 绑定器、函数对象、lambda表达式实际上都是函数对象。
- 如果我们最终得到的绑定器、函数对象、lambda表达式,这些函数对象,想在多条语句中应用 ,怎么办?如何将这些函数对象的类型留下来?

源码中希望你用一个函数类型来实例化function模板。
区别函数类型和函数指针类型:
函数指针类型:
- 是一个指针类型,指向返回值是void,不带形参的函数。

函数类型:
- 我们需要用函数类型来实例化function。
- 只给出返回值和参数列表即可

无参的hello1函数:


用函数对象类型func1将hello1函数类型保留下来了,然后就可以使用func1()调用了。
测试结果:

对于有参的hello2函数:


运行结果:

sum函数:


运行结果:

上面说明,function非常好用,可以直接将函数类型留下来。
function不仅仅可以留下函数类型,也可以留下函数对象类型。(函数对象的本质是()重载函数)
也可以说function是对一个函数/函数对象的包装。

结果:
上面是对function作用于全局函数,将其函数类型留下来。
当然,function也可以将类的成员方法留下来!

成员方法的调用必须依赖一个对象。

在调用时,第一个参数对应传入一个临时对象即可。

小结:

function使用举例
funtion的作用:
- 保留 全局函数、成员函数、其他函数对象的类型,然后再各处都可以使用。

我们使用function,将函数的类型保存下来使用


问题:这里也可以用函数指针来接收函数的类型吗?
- 可以接收函数类型;但是只能接收普通的函数,不能接收绑定器绑定的函数对象,也不能接收lambda表达式函数对象。

#include <iostream>
#include <vector>
#include <map>
#include <functional>//使用function函数对象类型
#include <algorithm>
#include <ctime>
#include <string>
using namespace std;
void doShowAllBooks() { cout << "查看所有书籍信息" << endl; }
void doBorrow() { cout << "借书" << endl; }
void doBack() { cout << "还书" << endl; }
void doQueryBooks() { cout << "查询书籍" << endl; }
void doLoginOut() { cout << "注销" << endl; }
int main()
{
int choice = 0;
map<int, function<void()>> actionMap;
actionMap.insert({ 1, doShowAllBooks });//老版本:insert(make_pair(xx,xx));
actionMap.insert({ 2, doBorrow });
actionMap.insert({ 3, doBack });
actionMap.insert({ 4, doQueryBooks });
actionMap.insert({ 5, doLoginOut });
for (;;)
{
cout << "-----------------" << endl;
cout << "1.查看所有书籍信息" << endl;
cout << "2.借书" << endl;
cout << "3.还书" << endl;
cout << "4.查询书籍" << endl;
cout << "5.注销" << endl;
cout << "-----------------" << endl;
cout << "请选择:";
cin >> choice;
auto it = actionMap.find(choice);//map pair first second
if (it == actionMap.end())
{
cout << "输入数字无效,重新选择!" << endl;
}
else
{
it->second();
}
//不好,因为这块代码无法闭合 无法做到“开-闭原则”
/*switch (choice)
{
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
break;
}*/
}
return 0;
}
1.4、模板的完全特例化和非完全特例化
有完全特例化,就选择对应的完全特例化,有部分特例化,就匹配部分特例化,没有的话,就原模板实例化
模板的完全特例化


能运用在int类型上,字符串比较就不行了。
此时的实例化compare代码就不行了,字符串比较的是地址,并不是字典的顺序。
需要多加一个模板的完全特例化:(完全特例化: 类型完全是已知的)
- template后面的<>什么参数都没有,表示参数都是已知的。

运行结果:

模板的部分特例化
原模板实例化:

完全特例化版本:
- template后面的<>什么参数都没有,表示参数都是已知的。

部分特例化版本: - vector后面知道它是一个指针类型,但不知道具体什么指针,template后面的<>指针类型。



函数指针的部分特例化:

函数类型的部分特例化:


区分函数类型和函数指针类型:

运行结果:


#include <iostream>
#include <cstdio>
using namespace std;
template<typename T>
class Vector
{
public:
Vector() { cout << "call Vector template init" << endl; }
};
//下面这个是对char*类型提供的完全特例化版本 #1
template<>//特例化的语法
class Vector<char*>
{
public:
Vector() { cout << "call Vector<char*> init" << endl; }
};
//下面这个是对指针类a型提供的部分特例化版本 #2
template<typename Ty>
class Vector<Ty*>
{
public:
Vector() { cout << "call Vector<Ty*> init" << endl; }
};
//指针函数指针(有返回值,有两个形参变量)提供的部分特例化
template<typename R, typename A1, typename A2>
class Vector<R(*)(A1, A2)>
{
public:
Vector() { cout << "call Vector<R(*)(A1, A2)> init" << endl; }
};
//针对函数(有一个返回值,有两个形参变量)类型提供的部分特例化
template<typename R, typename A1, typename A2>
class Vector<R(A1, A2)>
{
public:
Vector() { cout << "call Vector<R(A1, A2)> init" << endl; }
};
int sum(int a, int b) { return a + b; }
int main()
{
Vector<int> vec1;
Vector<char*> vec2;
Vector<int*> vec3;
Vector<int(*)(int, int)> vec4;
Vector<int(int, int)> vec5;//function
//注意区分一下函数类型和函数指针类型
//用函数指针定义的变量本身就是一个指针
typedef int(*PFUNC1)(int, int); //PFUNC1是函数指针类型
PFUNC1 pfunc1 = sum;
cout << pfunc1(10, 20) << endl;
typedef int PFUNC2(int, int); //PFUNC2是函数指针
//函数类型在定义的时候,需要将指针*加上
PFUNC2* pfunc2 = sum;
cout << (*pfunc2)(10, 20) << endl;
return 0;
}
模板的实参推演



程序中添加一个sum函数;


程序推导出:

问题: 上面直接退的函数类型,范围太大了,有返回值类型,还有形参,我们想将返回值和所有形参的类型都取出来;
我们直接用一个函数指针的模板部分特例化来做:

我们就可以得到细分的类型。


定义一个Test类,里面有一个成员函数sum。



得到一个指向成员方法的函数指针类型。

我们将其参数细分,添加func3函数
我们直接用一个成员方法函数指针的模板部分特例化来做:



#include <iostream>
#include <typeinfo>
using namespace std;
int sum(int a, int b) { return a + b; }
//T包含了所有的大的类型 返回值,所有形参的类型都取出来
template<typename T>
void func(T a)
{
cout << typeid(T).name() << endl;
}
template<typename R, typename A1, typename A2>
void func2(R(*a)(A1, A2))
{
cout << typeid(R).name() << endl;
cout << typeid(A1).name() << endl;
cout << typeid(A2).name() << endl;
}
class Test
{
public:
int sum(int a, int b) { return a + b; }
};
template<typename R, typename T, typename A1, typename A2>
void func3(R(T::* a)(A1, A2))
{
cout << typeid(R).name() << endl;
cout << typeid(T).name() << endl;
cout << typeid(A1).name() << endl;
cout << typeid(A2).name() << endl;
}
int main()
{
func(10);
func("aaa");
func(sum);//T int (*)(int,int) int (int,int)
func2(sum);
func(&Test::sum);//int (__thiscall Test::*)(int,int)
func3(&Test::sum);
return 0;
}
总结
- 这几个例子指导我们在写函数模板,类模板时,针对一个具体情况,把他们一个大类型的细分类型,定义相应的参数,接收大类型的所有细分类型,使用的就是模板部分偏特化。
1.5、function函数对象类型实现原理
通过两个例子:一个是持有普通函数,一个是持有类成员方法;查看类的成员方法到底是什么。
hello函数:




对于sum函数:




将上面两个模板特例化合并——使用可变参模板。
使用C++11中的可变参;

#include <iostream>
#include <typeinfo>
#include <string>
#include <functional>
using namespace std;
/*
function函数对象类型的实现原理
*/
void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }
///
template<typename Fty>
class myfunction {};
//模板的部分偏特化
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:
using PFUNC = R(*)(A1); //需要接受外部传进来的函数类型,使用函数指针接收
myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
R operator()(A1 arg)
{
return _pfunc(arg);//hello(arg)
}
private:
PFUNC _pfunc;
};
//模板的部分偏特化
template<typename R, typename A1, typename A2>
class myfunction<R(A1, A2)>
{
public:
using PFUNC = R(*)(A1, A2);
myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
R operator()(A1 arg1, A2 arg2)
{
return _pfunc(arg1, arg2);//hello(arg)
}
private:
PFUNC _pfunc;
};
//可变参模板
template<typename R, typename... A>//一组可变参数个数
class myfunction<R(A...)>
{
public:
using PFUNC = R(*)(A...);
myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
R operator()(A... arg)
{
return _pfunc(arg...);//hello(arg) 表示一组形参变量
}
private:
PFUNC _pfunc;
};
int main()
{
function<void(string)> func1(hello);
func1("hello world!");//func1.operator()("hello world!")
myfunction<int(int, int)> func2(sum);
cout << func2(10, 20) << endl;
return 0;
}
1.6、C++11 绑定器介绍
C++11 bind绑定器 => 返回的结果还是一个函数对象
bind占位符最多有20个参数。
C++11的bind和function比较:
- function可以直接将函数类型留下来。
- bind绑定器可以给函数绑定过固定的参数。

运行结果:

绑定器问题:
- 只能使用在语句中,除了语句就不能使用了;
如何将绑定器的类型留下来?
- 综合运用function和bind。
参数占位符
调用的时候,用户自己传入参数!!!
参数占位符placeholders::_1: 参数具体是什么,不知道,等待用户传递;(最多有20个占位参数)
- 因此在调用时,必须在()中传入参数;



绑定器问题:
- 只能使用在语句中,除了语句就不能使用了;
解决方法:
main函数中:
- 定义funtion,返回值是void,有一个string形参变量的函数类型,用来记录bind绑定器绑定的结果。
- 可以通过function函数对象类型,把绑定器的类型留下来,可以重复的去使用。


#include <iostream>
#include <typeinfo>
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include <thread>
using namespace std;
using namespace placeholders;
/*
C++11 bind绑定器 => 返回的结果还是一个函数对象
*/
void hello(string str) { cout << str << endl; }
int sum(int a, int b) { return a + b; }
class Test
{
public:
int sum(int a, int b) { return a + b; }
};
int main()
{
//bind是函数模板 可以自动推演模板类型参数
bind(hello, "hello bind!")();//bind绑定了一个"hello bind!"参数,
//返回的结果是绑定器,也就是函数对象, 需要调用它的()重载符号
cout << bind(sum, 10, 20)() << endl;
cout << bind(&Test::sum, Test(), 20, 30)() << endl;
//参数占位符 绑定器出了语句,无法继续使用
//只是占位的作用,调用的时候就要传递参数了
//书写的时候使用多少个占位符,就是意味着用户调用的时候要传入几个参数
bind(hello, _1)("hello bind 2!");
cout << bind(sum, _1, _2)(200, 300) << endl;
//此处把bind返回的绑定器binder就复用起来了
function<void(string)> func1 = bind(hello, _1);
func1("hello china!");
func1("liu feng ni hao");
return 0;
}
3167



被折叠的 条评论
为什么被折叠?



