STL中绑定器的使用

使用STL的过程中,有时适当地使用绑定器,能够使得代码更简洁、更高效。谈到绑定器,就不得不说说函数对象(function object),或称为仿函数(functor)。

C++中的函数对象是这样一种特殊的类型,它定义了操作符“()”,所以能够像函数调用一样在对象名后加上“()”,并传入对应的参数,从而执行相应的功能。这样的类型就是函数对象,STL中通常使用函数对象作为容器的排序规则并且在算法中广泛应用。如下面定义的类就是一个简单的函数对象:

class Functor

{

public:

    int operator()(int a, int b)

    {

        return a < b;

    }

};



int main()

{

    Functor f;

    int a = 5;

    int b = 7;

    int ans = f(a, b);

}

STL中的绑定器有类绑定器和函数绑定器两种,类绑定器有binder1st和binder2nd,而函数绑定器是bind1st和bind2nd。下面是STL在vs2008中的实现,可以看到类绑定器binder2nd是从模板类unary_function继承而来的,所以从根本上说它是一个一元函数对象,同理binder1st亦然。而bind2nd是一个全局的模板函数其返回值为一个binder2nd模板类的实例。所以本质上无论类绑定器还是函数绑定器都是用于构造一个一元的函数对象。

 

template<class _Fn2>

class binder2nd : public unary_function<typename _Fn2::first_argument_type,

			typename _Fn2::result_type>

{	// functor adapter _Func(left, stored)

public:

	typedef unary_function<typename _Fn2::first_argument_type,

		typename _Fn2::result_type> _Base;

	typedef typename _Base::argument_type argument_type;

	typedef typename _Base::result_type result_type;



	binder2nd(const _Fn2& _Func,

		const typename _Fn2::second_argument_type& _Right)

		: op(_Func), value(_Right)

	{	// construct from functor and right operand

	}



	result_type operator()(const argument_type& _Left) const

	{	// apply functor to operands

		return (op(_Left, value));

	}



	result_type operator()(argument_type& _Left) const

	{	// apply functor to operands

		return (op(_Left, value));

	}



protected:

	_Fn2 op;	// the functor to apply

	typename _Fn2::second_argument_type value;	// the right operand

};

// TEMPLATE FUNCTION bind2nd

template<class _Fn2,

	class _Ty> inline

binder2nd<_Fn2> bind2nd(const _Fn2& _Func, const _Ty& _Right)

{	// return a binder2nd functor adapter

	typename _Fn2::second_argument_type _Val(_Right);

	return (std::binder2nd<_Fn2>(_Func, _Val));

}

那么到底什么是绑定器呢?它有什么具体的用途?MSDN中对绑定器的解释是:“an adaptor to convert a binary function object into a unary function object by binding the first/second argument of the binary function to a specified value”,意思是绑定器是一种适配器,用于将二元函数对象的第一或者第二个参数绑定为一个特定的值的方式来实现二元函数对象向一元函数对象的转换。所以,绑定器的实质是一个函数适配器,它能够实现将二元函数对象转换为一元函数对象。在很多STL的算法函数中,需要给某个参数传入一个一元的函数对象,但有些时候由于实际的需要,直接定义一个一元函数对象并不是很直观有效,此时应用绑定器或许会有意外的收获。      

下面的例子给出了一个可能应用到绑定器的实际情境。在一个序列容器,如vector或者list中,存储着结构体或者类的实例对象,或者是它的指针形式,现在的需求是,需要在这样的序列容器中快速找出结构体或者类这样的复合类型对象中的某个成员的值满足一定条件的某个对象或其指针的值。

让我们先来看看函数find_if的原型,可以看到前两个参数是输入迭代器,表明了查找的区间,第三个参数则是一个一元谓词。所谓谓词即是一种特殊的函数对象,它的返回值始终为bool类型,对应于函数对象便有一元谓词和二元谓词之分。对于find_if来说,一元谓词的唯一参数其类型必须是序列容器中元素的类型。

template<class InputIterator, class T, class Predicate> inline

   InputIterator find_if(

      InputIterator First,

      InputIterator Last,

      Predicate Predicate

   )

类似的其他函数如count_if、remove_if等都需要这样的谓词,通常利用这些函数的自定义谓词版本来快速查找序列容器中符合条件的元素时,代码可以写得很elegant。

bool IsObjEquals(int n)

{

   if (n%2) return true;

}



remove_if(MyList.begin(), MyList.end(), IsObjEquals());

然而,当需要在复合类型对象中找出某个满足条件的元素,其某个成员值满足相应的条件时,单纯的定义一个一元谓词是无能为力的。此时一元的函数对象难以表达复合对象和目标类型之间的关系,想像中可以定义类似于如下这样的二元函数对象(二元谓词)来实现。

struct IsObjIDEquals : binary_function<COMPOSEOBJ, int, bool> 

{

    bool operator()(const COMPOSEOBJ &_left, int _right) const

    {

          if (_left.nID == _right)

          {

                  return true;

          }

          else

          {

                  return false;

          }

    }

};

但是,事情并不是这么简单,find_if等函数只接受一元谓词,而且需要查找的复合类型中某个成员的值在一次运算(查找、计数或者删除等)过程中是恒定的。如何将这样的二元谓词转换为一元谓词呢?绑定器此时可以发挥它的威力了。当在一次查找(find_if)、计数(count_if)或删除(remove_if)操作中,复合类型中的单个成员值总是相对恒定的,所以可以利用bind2nd通过绑定二元函数对象中的第二个参数的方式来实现二元谓词向一元谓词的转换。于是运算的代码可以如下例这样写,同样很elegant。

remove_if(MyList.begin(), MyList.end(),bind2nd(IsObjIDEquals(), 3));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
一、概述 函数适配是一种可以改变函数行为的工具。在STL,函数适配通常用来将一个函数的参数或返回值类型改变为另一种类型,或者将一个函数的参数或返回值进行逻辑转换。 二、函数适配的种类 1. bind1st和bind2nd bind1st和bind2nd是两个函数适配,它们用来将一个二元函数转换为一个一元函数。bind1st将第一个参数绑定为指定值,而bind2nd将第二个参数绑定为指定值。 例如,在STL,有一个二元函数less,用于比较两个值的大小。我们可以使用bind1st将其转换为一个只比较一个值和指定值的一元函数: ```c++ #include <functional> #include <iostream> using namespace std; int main() { int a = 5; less<int> l; cout << l(3, a) << endl; // true bind1st(l, a)(3); // false return 0; } ``` 2. not1和not2 not1和not2也是两个函数适配,它们用来将一个一元函数或二元函数的返回值取反。 例如,在STL,有一个一元函数not1,用于将一个bool值取反。我们可以使用not1将一个返回bool值的一元函数的返回值取反: ```c++ #include <functional> #include <iostream> using namespace std; int main() { bool b = true; not1<less<int> > n; cout << n(3, 5) << endl; // false not1<logical_and<bool> > n2; cout << n2(true, false) << endl; // true return 0; } ``` 3. negate negate是一个函数适配,它用来将一个一元函数的返回值取负。 例如,在STL,有一个一元函数negate,用于将一个数值取负。我们可以使用negate将一个返回数值的一元函数的返回值取负: ```c++ #include <functional> #include <iostream> using namespace std; int main() { int a = 5; negate<int> n; cout << n(a) << endl; // -5 return 0; } ``` 4. plus、minus、multiplies、divides、modulus plus、minus、multiplies、divides、modulus是五个函数适配,它们用于将两个数值进行加、减、乘、除、取余运算。 例如,在STL,有一个二元函数plus,用于将两个数值相加。我们可以使用plus将两个返回数值的一元函数的返回值相加: ```c++ #include <functional> #include <iostream> using namespace std; int main() { int a = 5; plus<int> p; cout << p(a, 3) << endl; // 8 return 0; } ``` 5. compose1 compose1是一个函数适配,它用于将一个一元函数f和一个一元函数g组合成一个新的一元函数f(g(x))。 例如,在STL,有一个一元函数negate,用于将一个数值取负,和一个一元函数abs,用于将一个数值取绝对值。我们可以使用compose1将这两个一元函数组合成一个新的一元函数abs(negate(x)): ```c++ #include <functional> #include <iostream> using namespace std; int main() { int a = -5; compose1(negate<int>(), abs<int>())(a); // 5 return 0; } ``` 6. compose2 compose2是一个函数适配,它用于将一个二元函数f和两个一元函数g1、g2组合成一个新的二元函数f(g1(x), g2(y))。 例如,在STL,有一个二元函数plus,用于将两个数值相加,和两个一元函数negate和abs。我们可以使用compose2将这三个函数组合成一个新的二元函数abs(negate(x)) + abs(negate(y)): ```c++ #include <functional> #include <iostream> using namespace std; int main() { int a = -5, b = -3; compose2(plus<int>(), compose1(negate<int>(), abs<int>()), compose1(negate<int>(), abs<int>()))(a, b); // 8 return 0; } ``` 三、小结 函数适配是一种强大的工具,可以改变函数的行为,使得我们可以更加灵活地使用STL的函数。在实际应用,我们可以根据具体的需求选择合适的函数适配来实现所需的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值