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

时间: 2024-10-07 14:58:37

STL源码解析之bind1st和bind2nd的相关文章

STL 源码解析

一开始一直无法理解STL中的内存分析,一天很困,翻开了侯捷的STL源码解析,阅读一二,这是一针见血,字字珠玑,解开了一个又一个迷惑~ 简单记录,和大家分享一下 1) 空间适配器 template <class T1,class T2> inline void _construct(T1 *p,const T2 &value) { new (p) T1(value);  // placement new, and allocate object on the requested memo

《STL源码解析》读书笔记之序列式容器(2)

1.deque deque和vector的最大差异在于deque允许在常数时间内对首端进行元素的插入和删除操作.而且deque没有容量的观念,因为它是动态地以分段连续空间组合而成的,随时可以增加一段新的空间并链接起来.像vector那样因旧空间不足而重新配置一块更大空间的情况在deque里是不会发生的.虽然deque也提供Random Access Iterator,但它的迭代器并不是普通指针,这影响了很多操作的效率. (1)deque的map deque在逻辑上是连续空间,但实际上它是由一段一

《STL源码解析》读书笔记之allocator(1)

内存配置器allocator是stl中的一个模板类,它提供类型化的内存分配以及释放操作.SGI STL的配置器与众不同,其名称是alloc而非allocator,而且不接受任何参数(如vector<int,std::alloc>).每个SGI STL容器采用的默认内存配置其都是alloc而不是allocator. 首先介绍allocator.SGI中虽然有allocator的定义,但基本上从不用它,原因是效率不高.它仅仅是把c++的new和delete稍微做了一点封装而已(allocate函数

《STL源码解析》读书笔记之序列式容器(1)

1.vector vector的数据安排以及操作方式与array非常相似,两者的唯一差别在于空间的运用的灵活性.array是静态空间,一旦配置了就不能再改变.vector是动态空间,随着元素的加入它的内部机制会自行扩充空间以容纳新元素. (1)vector的迭代器 因为vector维护的是一个连续线性空间,所以无论其元素型别为何,普通指针都可以作为vector的迭代器.因为vector迭代器所执行的操作行为,如operator*,operator->,operator++,operator--,

STL sort 源码解析

前言 --本文整理自<STL源码解析> 虽然源码解析的代码比较老但是核心思想并没有太多变化并且直接看源码有太多细节我又看不懂最新的. 简介 sort接受两个RandomAccessIterators(随机存储迭代器),然后将区间内的所有元素以渐増的方式由小到大重新排列,第二个版本允许用户指定一个仿函数作为排序标准,STL所有关系型容器都拥有自动排序功能,不需要sort,stack,queue,priority-queue都有特别出入口,不允许排序,剩下vector,deque和list,前两者

Python2 基本数据结构源码解析

Python2 基本数据结构源码解析 Contents 0x00. Preface 0x01. PyObject 0x01. PyIntObject 0x02. PyFloatObject 0x04. PyStringObject 0x05. PyListObject 0x06. PyDictObject 0x07. PyLongObject 0x00. Preface 一切皆对象,这是Python很重要的一个思想之一,虽然在语法解析上有些细节还是不够完全对象化,但在底层源码里,这个思想还是贯穿

STL源码剖析

花了两天时间略读了一下<stl源码分析>,看了个大体,对于细节并没有深究.之所以想翻翻这本书,主要是想看看stl中的特性.适配器的具体实现.看完之后收获还是蛮大的,模板的各种组合让我眼前一亮,下面大概总结一些内容. 1.内存分配:sgi内存分配采用两级实现,对于大内存块的申请(大于128k)由第一级实现,第一级实现较简单,直接调用malloc和free.对于小于128k的内存请求,由第二级实现.第二级使用了内存池,维护了一些链表,分别指向不同大小的空闲内存块,这些内存块都是8的倍数的大小,分别

STL 源码剖析 算法 stl_algo.h -- partition

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie partition ------------------------------------------------------------------------ 描述:partition 会将区间[first,last) 中的元素重新排列.所有被一元条件运算 pred 判定为 true 的元素,都会被放在区间的前段, 被判定为 false 的元素,都会被放在区间的后段. partiti

ChrisRenke/DrawerArrowDrawable源码解析

转载请注明出处http://blog.csdn.net/crazy__chen/article/details/46334843 源码下载地址http://download.csdn.net/detail/kangaroo835127729/8765757 这次解析的控件DrawerArrowDrawable是一款侧拉抽屉效果的控件,在很多应用上我们都可以看到(例如知乎),控件的github地址为https://github.com/ChrisRenke/DrawerArrowDrawable