文章目录
前言
我在前面的文章中介绍了C++中的仿函数和函数适配器,这两个技术在STL中用的很广泛。今天我们研究下STL中的unary_function、binary_function、not1、bind2nd是如何实现的。
一、unary_function、binary_function
这两个函数比较简单,只是对形参和返回值加了别名,也就是typedef。下面看看源代码。
template<typename argType,typename resType>
struct unary_function
{
typedef argType argument_type;
typedef resType result_type;
};
template<typename arg1Type,typename arg2Type,typename resType>
struct binary_function
{
typedef arg1Type first_argument_type;
typedef arg2Type second_argument_type;
typedef resType result_type;
};
你可能会纳闷,为什么要定义这两个没有任何成员的类。这关系到后续的函数适配器,适配器都是基于这两个类的。
二、bind2nd
bind2nd是一个函数适配器,它的作用是可以绑定第二参数;可以被适配的对象必须是继承了binary_function的。这就跟前面的联系起来了。
1.函数适配器的定义
函数适配器也可以叫函数修饰器,它可以给仿函数增加一些修饰功能,最后表现出来也是仿函数的样子。此处可类比设计模式中的修饰者模式。
我的理解函数适配器最重要的是两个函数,一个是构造函数,一个是Operator()重载。
Operator()重载没啥好讲的,函数适配器适配的对象是仿函数,适配完成后,还得是一个仿函数。
函数适配器的构造函数,需要接受一个仿函数和可能的参数 (有没有参数,看改适配器的功能),保存仿函数参数,供Operator()重载函数中使用
bind2nd 代码如下:
template<typename OP>
class bind2nd : public unary_function<typename OP::first_argument_type,typename OP::result_type>
{
protected:
OP op;
typename OP::second_argument_type arg;
public:
bind2nd(const OP& x,typename const OP::second_argument_type& y):op(x),arg(y){}
typename OP::result_type Operator()(typename const OP::first_argument_type& x)
{
return op(x,arg);
}
}
关于以上的代码,有几个要点:
1.bind2nd构造函数接收一个仿函数和一个参数,该参数作为绑定的第二参数。
2.bind2nd只能适配继承了binary_function类的仿函数,因为构造函数和()重载函数都是用binary_function定义的类型。
比如:typename OP::second_argument_type arg; 等 typename是告诉编译器,后面跟的是一个类型。
3.()重载函数接收仿函数的第一参数,跟构造函数中接收的第二参数一起,作为仿函数的参数进行定义。
4.为了让bind2nd可以被进一步修饰,所以继承了unary_function。为什么不是binary_function呢?因为第二参数被绑定了,所以单参数的仿函数了。
2.bind2nd实例
代码如下:
先定义一个两参数的仿函数
template<typename T>
class greater : public binary_function<T,T,bool>
{
public:
bool Operator()(const T& a,const T& b)
{
return a>b;
}
}
该函数是比较两个数谁大,其实这样说不准确,不一定是两个数。因为模板仿函数,可以传入任意的实现了大于号运算符的类。
3.使用bind2nd修饰
#include <iostream>
using namespace std;
void main()
{
int x = 10,y = 20;
cout << greater<int>(x,y) << endl;
cout << bind2nd<greater<int>>(greater<int>(),5)(10) << endl;
}
bind2nd<greater>(greater(),5) 这一串代码就是把greater的第二参数绑定成5了。然后把10当成第二参数传入,进行greater的比较。
看着这句代码是不是很长,后面我们可以用模板类的参数类型推断功能,去改写这句代码。
4.改造bind2nd
template<typename OP,typename T>
bind2nd<OP> bind2nd_func(const OP& op,const T& arg)
{
typedef typename OP::second_argument_type stype;
return bind2nd<OP>(op,stype(arg));
}
bind2nd_func函数的功能是帮助我们去生成bind2nd。 对于类型T的要求是能够用T类型的变量,初始化OP::second_argument_type类, “stype(arg)” 这句话就是这个要求。
改造后的调用代码,如下:
#include <iostream>
using namespace std;
void main()
{
int x = 10,y = 20;
cout << greater<int>(x,y) << endl;
cout << bind2nd_fun(greater<int>(),5)(10) << endl;
}
这一段比较绕,大家静下心来,好好理解一下。
三、not1
not1也是一个函数适配器,它修饰继承了unary_function的仿函数。作用是在原始操作上进行取反。
1.not1定义
它的构造函数接收一个仿函数,Operator()重载接收仿函数的参数。
源代码如下:
template<typename OP>
class not1 : public myunaryfunction<typename OP::argument_type,bool>
{
protected:
OP op;
public:
explicit not1(const OP& x) : op(x) {}
bool operator()(const typename OP::argument_type& a)
{
return !op(a);
}
};
2.改造not1
跟bind2nd函数一样,我们也需要一个模板函数来生成not1类。
代码如下:
template<typename OP>
not1<OP>& not1_fun(const OP& op)
{
return not1<OP>(op);
}
3.使用not1
代码如下:
#include <iostream>
using namespace std;
void main()
{
int x = 10,y = 20;
cout << greater<int>(x,y) << endl;
if(not1_fun(bind2nd_fun(greater<int>(),5))(10) == true)
cout << "true" << endl;
else
cout << "false" << endl;
}
总结
本篇比较详细的介绍了仿函数和函数适配器。