STL源码解析之bind1st和bind2nd
首先我们先来了解一下一元函数和二元函数。
一元函数数学上一般形式表示为 z = f(x),只有一个变量x。
二元函数数学上一般形式表示为 z = f(x,y),存在两个变量,分别是x和y。
STL中为了描述一元函数和二元函数,定义了两个结构体来描述。如下:
//一元函数结构 template <class Arg, class Result> struct unary_function { typedef Arg argument_type; //参数类型,可以理解为x对应的类型 typedef Result result_type;//返回值类型,可以理解为 z 对应的类型 };
//二元函数结构 template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; //第一个参数类型,可以理解为x对应的类型 typedef Arg2 second_argument_type;//第二个参数类型,可以理解为y对应的类型 typedef Result result_type; //返回值类型,可以理解为 z 对应的类型 };
接下来我们来看看,一元函数结构和二元函数结构在STL中用来定义实际一元函数和二元函数的例子,如下
//取负 template <class T> struct negate : public unary_function<T, T> { T operator()(const T& x) const { return -x; } }; //小于 template <class T> struct less : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x < y; } };
知道了STL中一元函数和二元函数的具体实现,我们就可以来看下bind1st的具体定义,如下:
template <class Operation, class T> inline binder1st<Operation> bind1st(const Operation& op, const T& x) { typedef typename Operation::first_argument_type arg1_type; return binder1st<Operation>(op, arg1_type(x)); }
参数op为Operation类型的函数对象,参数x为类型T的对象,而bind1st函数的返回值实际是构造了一个binder1st的对象。
那我们接着看下binder1st的具体定义,源码如下:
template <class Operation> class binder1st: public unary_function<typenameOperation::second_argument_type,typename Operation::result_type> { protected: Operation op;//代表操作语义的二元函数对象,可以理解为上文中的函数f typename Operation::first_argument_type value;//绑定的参数类型对应的对象 public: binder1st(const Operation& x,const typenameOperation::first_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const
{ return op(value, x); } };
由此我们可以看出bind1st(op,value);实际是构造了一个binder1st类的对象,对应的操作是op,绑定值value,
binder1st类重载函数调用操作符(),实际调用的时候相当于binder1st(op,value)(x);从上面重载的实际实现我们可以看出,
这个实际上就是op(value,x);即绑定了value值作为二元函数op的第一个参数,而此时x变量为第二个参数。
bind2nd的源码如下:
template <class Operation, class T> inline binder2nd<Operation> bind2nd(const Operation& op, const T& x)
{ typedef typename Operation::second_argument_type arg2_type; return binder2nd<Operation>(op, arg2_type(x)); }
实际就是构造一个binder2nd对象,而binder2nd的实现如下:
template <class Operation>
class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type>
{ protected: Operation op; typename Operation::second_argument_type value; public: binder2nd(const Operation& x,const typename Operation::second_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const
{ return op(x, value); } };
对照上面对binder1st类的描述,实际bind2nd(op,value);的形式等价于binder2nd(op,value);而实际调用的形式
binder2st(op,value)(x);就等价于op(x,value);
下面通过一个例子来区分一下,bind1st和bind2nd在使用上区别。
我们要从一个存放学生成绩的容器中分别统计分数大于等于60分的个数,以及小于60分的学生个数。
我们讲通过STL里面的条件统计函数count_if和二元函数来完成上面的要求。
#include <iostream> #include <vector> #include <algorithm> #include <functional> using namespace std; int main() { vector<int> vec ; vec.push_back(40);vec.push_back(50); vec.push_back(60);vec.push_back(70); vec.push_back(80);vec.push_back(90); //统计及格个数 binder1st<less_equal<int> > binder1 = bind1st(less_equal<int>(),60);//==>binder1st(less,60); int nPassCnt = count_if(vec.begin(), vec.end(), binder1 );//==>less_equal(60,x)==> 60 <= x //统计不及格个数 binder2nd<less<int> > binder2 = bind2nd(less<int>(), 60);//==>binder2nd(less,60); int nFailCnt = count_if(vec.begin(), vec.end(), binder2 );//==>less(x,60);==> x < 60 cout << "nPassCnt:" << nPassCnt << endl; cout << "nFailCnt:" << nFailCnt << endl; return 0; }
运行结果:
nPassCnt:4
nFailCnt:2