STL源码解析之bind1st和bind2nd

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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

 

以上是关于STL源码解析之bind1st和bind2nd的主要内容,如果未能解决你的问题,请参考以下文章

STL sort 源码解析

转 bind1st bind2nd的使用

STL 之 list源码自行实现(iterator)

STL 源码剖析笔记之仿函数

STL之vector

STL源码剖析之allocator