STL源码剖析(仿函数/bind2nd)

Posted Runnyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL源码剖析(仿函数/bind2nd)相关的知识,希望对你有一定的参考价值。

仿函数(functors)其实就是重载了operator()的对象。

下面简单先看看它的一个例子:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<typename T>
 5 struct m_plus
 6 {
 7     T operator()(const T& x, const T& y) { return x + y; }
 8 };
 9 
10 int main(int argc, char *argv[])
11 {
12     // 定义其对象  调用其operator()
13     m_plus<int> op;
14     cout << op(1, 2) << endl;
15     // 产生一个匿名对象  这是仿函数的主流用法
16     cout << m_plus<int>()(1, 2) << endl;
17     return 0;
18 }
View Code

 

既然仿函数跟函数的用法类同,那为什么不直接使用函数指针代替呢?

个人认为有两个原因

1.仿函数可以有自己的状态,而函数指针则不行(有的使用template或者static变量可以实现)。

我们可以这样子使用仿函数:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<typename T, T add>
 5 struct m_plus
 6 {
 7     m_plus() { _add = add; }
 8     T operator()(const T& x) { return x + _add; }
 9     // 仿函数可以具有自己的状态
10     int _add;
11 };
12 
13 int main(int argc, char *argv[])
14 {
15     m_plus<int, 10> op;
16     cout << op(100) << endl;
17     cout << op(200) << endl;
18     return 0;
19 }
View Code

 

2.仿函数可以与函数适配器搭配使用。

举一个例子,例如我们如果要使用count_if算法来计算容器中大于10的元素的个数。

如果我们使用greater<int>作为判别式(二元),而count_if只接受一个一元判别式,这时候我们就需要搭配函数适配器一起使用了。

而函数指针不能直接搭配函数适配器一起使用,具体在分析bind2nd的时候会讲到。

 1 #include <iostream>
 2 #include <vector>
 3 #include <functional>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 
 8 int main(int argc, char *argv[])
 9 {
10     vector<int> coll{ 1, 3, 5, 7, 9, 11, 13, 15 };
11     // 接着下面有bind2nd的具体实现
12     cout << count_if(coll.begin(), coll.end(), bind2nd(greater<int>(), 10)) << endl;
13     return 0;
14 }
View Code

 

bind2nd

bind2nd可以将二元仿函数转化为一元仿函数,这看上去好像很神奇,其实它的实现很简单。

首先,二元仿函数会继承自binary_function,其实只是一些typedef,这些都将用于函数适配器。

 1 template <class Arg1, class Arg2, class Result>
 2 struct binary_function {
 3     typedef Arg1 first_argument_type;
 4     typedef Arg2 second_argument_type;
 5     typedef Result result_type;
 6 };   
 7 
 8 template <class T>
 9 struct greater : public binary_function<T, T, bool> {
10     bool operator()(const T& x, const T& y) const { return x > y; }
11 };
View Code

bind2nd将二元仿函数跟第二个参数型别作为模板型别,下面是具体实现:

 1 template <class Operation, class T>
 2 inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
 3     typedef typename Operation::second_argument_type arg2_type;
 4     // 调用binder2nd
 5     return binder2nd<Operation>(op, arg2_type(x));
 6 }
 7 
 8 // binder2nd是一个一元仿函数(本身可以搭配函数适配器一起使用)
 9 template <class Operation> 
10 class binder2nd
11   : public unary_function<typename Operation::first_argument_type,
12                           typename Operation::result_type> 
13 {
14 protected:
15     // 传进来的二元仿函数
16     Operation op;
17     // 传进来的第二个参数  因为仿函数内部可以typedef  而函数指针则不行
18     // 因此只能适配仿函数  而不能适配函数指针
19     typename Operation::second_argument_type value;
20 public:
21     // 构造函数
22     binder2nd(const Operation& x,
23             const typename Operation::second_argument_type& y) 
24        : op(x), value(y) {}
25 
26     // 直接调用二元仿函数 
27     typename Operation::result_type
28     operator()(const typename Operation::first_argument_type& x) const {
29         return op(x, value); 
30     }
31 };
View Code

 

以上是关于STL源码剖析(仿函数/bind2nd)的主要内容,如果未能解决你的问题,请参考以下文章

《STL源码剖析》——第七八章:仿函数与接配器

stl源码剖析 为啥要使用仿函数

STL源码剖析

STL源码解析之bind1st和bind2nd

Sword STL之仿函数概念介绍

STL源码剖析 学习笔记