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 }
既然仿函数跟函数的用法类同,那为什么不直接使用函数指针代替呢?
个人认为有两个原因
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 }
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 }
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 };
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 };
以上是关于STL源码剖析(仿函数/bind2nd)的主要内容,如果未能解决你的问题,请参考以下文章