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 };

时间: 2024-12-18 04:38:54

STL源码剖析(仿函数/bind2nd)的相关文章

STL&quot;源码&quot;剖析-重点知识总结

STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合套用: 容器(Containers):各种数据结构,如:vector.list.deque.set.map.用来存放数据.从实现的角度来看,STL容器是一种class template. 算法(algorithms):各种常用算法,如:sort.search.copy.erase.从实现的角度来看,STL算法

通读《STL源码剖析》之后的一点读书笔记

[QQ群: 189191838,对算法和C++感兴趣可以进来] 直接逼入正题. Standard Template Library简称STL.STL可分为容器(containers).迭代器(iterators).空间配置器(allocator).配接器(adaptors).算法(algorithms).仿函数(functors)六个部分. 迭代器和泛型编程的思想在这里几乎用到了极致.模板或者泛型编程其实就是算法实现时不指定具体类型,而由调用的时候指定类型,进行特化.在STL中,迭代器保证了ST

STL源码剖析 --- 空间配置器 std::alloc

STL是建立在泛化之上的.数组泛化为容器,参数化了所包含的对象的类型.函数泛化为算法,参数化了所用的迭代器的类型.指针泛化为迭代器,参数化了所指向的对象的类型.STL中的六大组件:容器.算法.迭代器.配置器.适配器.仿函数. 这六大组件中在容器中分为序列式容器和关联容器两类,正好作为STL源码剖析这本书的内容.迭代器是容器和算法之间的胶合剂,从实现的角度来看,迭代器是一种将operator*.operator->.operator++.operator-等指针相关操作予以重载的class tem

《STL源码剖析》---stl_hashtable.h阅读笔记

在前面介绍的RB-tree红黑树中,可以看出红黑树的插入.查找.删除的平均时间复杂度为O(nlogn).但这是基于一个假设:输入数据具有随机性.而哈希表/散列表hash table在插入.删除.查找上具有"平均常数时间复杂度"O(1):且不依赖输入数据的随机性. hash table的实现有线性探测.二次探测.二次散列等实现,SGI的STL是采用开链法(separate chaining)来实现的.大概原理就是在hash table的每一项都是个指针(指向一个链表),叫做bucket.

【转载】STL&quot;源码&quot;剖析-重点知识总结

原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合套用: 容器(Containers):各种数据结构,如:vector.list.deque.set.map.用来存放数据.从实现的角度来看,STL容器是一种class template. 算法(algorithms):各种常用算法,如:sort.se

STL源码剖析——STL算法之find查找算法

前言 由于在前文的<STL算法剖析>中,源码剖析非常多,不方便学习,也不方便以后复习,这里把这些算法进行归类,对他们单独的源码剖析进行讲解.本文介绍的STL算法中的find.search查找算法.在STL源码中有关算法的函数大部分在本文介绍,包含findand find_if.adjacent_find.search.search_n.lower_bound. upper_bound. equal_range.binary_search.find_first_of.find_end相关算法,下

STL源码剖析——STL算法之remove删除算法

前言 由于在前文的<STL算法剖析>中,源码剖析非常多,不方便学习,也不方便以后复习,这里把这些算法进行归类,对他们单独的源码剖析进行讲解.本文介绍的STL算法中的remove删除算法,源码中介绍了函数remove.remove_copy.remove_if.remove_copy_if.unique.unique_copy.并对这些函数的源码进行详细的剖析,并适当给出使用例子,具体详见下面源码剖析. remove移除算法源码剖析 // remove, remove_if, remove_co

STL源码剖析——STL算法stl_algo.h

前言 在前面的博文中剖析了STL的数值算法.基本算法和set集合算法,本文剖析STL其他的算法,例如排序算法.合并算法.查找算法等等.在剖析的时候,会针对函数给出一些例子说明函数的使用.源码出自SGI STL中的<stl_algo.h>文件.注:本文的源码非常多,可能后续博文会对这些算法进行归类分析. STL算法剖析 #ifndef __SGI_STL_INTERNAL_ALGO_H #define __SGI_STL_INTERNAL_ALGO_H #include <stl_heap

《STL源码剖析》---stl_numeric.h阅读笔记

stl_numeric.h里面的都是数值算法,与数值计算有关. G++ 2.91.57,cygnus\cygwin-b20\include\g++\stl_numeric.h 完整列表 /* * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any pu