ptr_fun是将一个普通的函数适配成一个仿函数(functor), 添加上argument_type和result type等类型,它的定义如下:
template<class _Arg1,
class _Arg2,
class _Result> inline
pointer_to_binary_function<_Arg1, _Arg2, _Result,
_Result(__clrcall *)(_Arg1, _Arg2)>
ptr_fun(_Result (__clrcall *_Left)(_Arg1, _Arg2))
{ // return pointer_to_binary_function functor adapter
return (pointer_to_binary_function<_Arg1, _Arg2, _Result,
_Result (__clrcall *)(_Arg1, _Arg2)>(_Left));
}
下面的例子就是说明了使用ptr_fun将普通函数(两个参数, 如果有多个参数, 要改用boost::bind)适配成bind1st或bind2nd能够使用的functor,否则对bind1st或bind2nd直接绑定普通函数,则编译出错。
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int sum(int arg1, int arg2)
{
std::cout<< "arg1 = " << arg1 << std::endl;
std::cout<< "arg2 = " << arg2 << std::endl;
int sum = arg1 + arg2;
std::cout << "sum = " << sum << std::endl;
return sum;
}
int main(int argc, char *argv[], char *env[])
{
bind1st(ptr_fun(sum), 1)(2); // the same as sum(1,2)
bind2nd(ptr_fun(sum), 1)(2); // the same as sum(2,1)
return 0;
}
移除性算法
本系列所有算法根据元素值或某一准则,在一个区间内移除某些元素。这些算法并不能改变元素的数量,它们只是以逻辑上的思考,将原本置于后面的“不移除元素”向前移动,覆盖那些被移除元素而已,它们都返回新区间的逻辑终点(也就是最后一个“不移除元素”的下一位置)。
移除某些特定元素
remove(beg,end,cosntt T& value)
remove_if(beg,end,op)
(1)remove()会移除区间{beg,end)中每一个“与value相等”的元素;
(2)remove_if()会移除区间[beg,end)中每一个“令以下一元判断式:op(elem)获得true”的元素;
(3)两个算法都返回变动后的序列的新逻辑终点(也就是最后一个未被移除元素的下一个位置);
(4)这些算法都是把原本置于后的的未移除元素向前移动,覆盖被移除元素;
(5)未被移除的元素在相对次序上保持不变;
(6)调用者在调用此算法之后,应保证从此采用返回的新逻辑终点,而不再使用原始终点end;
(7)op不应该在函数调用过程中改变自身状态;
(8)由于会发生元素变动,所以这些算法不可用于关联式容器,关联式容器提供了功能相似的成员函数erase();
(9)list提供了一个等效成员函数remove():不是重新赋值元素,而是重新安排指针,因此具有更加性能;
(10)复杂性:线性;
代码示例:
#include<iostream>
#include<list>
#include<algorithm>
using namespace std;
void main()
{
int myints2[] = {1,2,3,4,5,6,7,8,9};
list<int> li(myints2,myints2+9);
list<int>::iterator plend,il;
cout << "原始数据:";
for(il=li.begin(); il!=li.end(); ++il)
cout<<*il<<" ";
cout << endl;
cout << "移除后的数据";
plend = remove_if (li.begin(),li.end(),bind2nd(modulus<int>(),2));//删除奇数,返回未被移除元素的下一个位置
for(il=li.begin(); il!=plend; ++il)
cout<<*il<<" ";
cout << endl;
cout<<"li中数据";
li.erase(remove_if (li.begin(),li.end(),bind2nd(modulus<int>(),2)),li.end());//要删除需要调用erase方法
for(il=li.begin(); il!=li.end(); ++il)
cout<<*il<<" ";
cout << endl;
cout<<"表示并没有删除数据而是向前覆盖!"<<endl;
cout<<endl;
}
运行结果:
提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的基本算法,迭代器是由容器提供的一种接口,算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件,函数对象(Function Object,JJHou译作Functor仿函数)。
什么是函数对象
顾名思义,函数对象首先是一个对象,即某个类的实例。其次,函数对象的行为和函数一致,即是说可以像调用函数一样来使用函数对象,如参数传递、返回值等。这种行为是通过重载类的()操作符来实现的,举例说明之,
|
其实我们早就开始使用函数对象了,当你写下sort(v.begin(), v.end())时(假定v是vector<int>),其实调用的是sort(v.begin(), v.end(), less<int>()),这样sort就会将v从小至大排序。若要逆向排序,你就需要显式地为sort指定一个排序规则,即函数对象greater<int>(). less<T>和greater<T>是STL中的两个模板类,它们使用类型T的<和>操作符。less<T>的一个典型实现可能是这样的:
|
函数对象的分类
根据用途和参数特征,STL中的函数对象通常分为以下几类:Predicates, Arithmetic Function Objects, Binders, Negaters, Member Function Adapters, Pointer to Function Adapters。下面逐一介绍一下,之前得先介绍两个基类:
|
使用这两个基类,首先需要包含头文件。
Predicates
Predicate是一种函数对象,返回值(应该是operator()的返回值)为布尔型,接受一个或者两个参数。通常用来判断对象的有效性(一个参数时)或者对两个对象进行比较(如less)。你可以根据自己的需要定义自己的Predicate,但STL已经定义了一些Predicate,你可以直接使用。
Predicate | 类型 | 描述 |
---|---|---|
equal_to() | Binary | 使用==判等 |
not_equal_to() | Binary | 使用!=判等 |
less() | Binary | 使用< |
greater() | Binary | 使用> |
less_equal() | Binary | 使用<= |
greater_equal() | Binary | 使用>= |
logical_not() | Unary | 使用!逻辑取反 |
logical_and() | Binary | 使用&&逻辑与 |
logical_or() | Binary | 使用||逻辑或 |
算术运算函数对象
进行简单的算术运算,这类函数对象我用的很少,通常是自己定义。
函数对象 | 类型 | 描述 |
---|---|---|
negate() | Unary | 使用-求负 |
plus() | Binary | 使用+加法 |
minus() | Binary | 使用-减法 |
multiplies() | Binary | 使用*乘法 |
divides() | Binary | 使用/除法 |
modulus() | Binary | 使用%求余 |
绑定Binders
有两种绑定bind1st和bind2nd,它们可以将一个二元函数对象的其中一个参数绑定为某个已知的对象,从而得到一个一元函数对象。例如要在vector<int> v中查找等于372的值的位置,我可以将372绑定到equal_to<int>()的第一个或者第二个参数:
|
其实,这里的bind1st和bind2nd并不是函数对象,只是模板函数而已。这两个函数分别返回类型为binder1st和binder2nd的函数对象。下面的代码,聪明的你肯定一看就懂:
|
Negaters
Negater是针对Predicate设计的,它简单的将Predicate的返回值取反。有两个Negater,not1和not2,分别对一元和二元Predicate取反。
Member Function Adapters
有时候,你可能想让算法调用容器元素的成员函数,而不是外部函数。因为外部无法改变对象内的状态,且内部函数通常具有更高的效率。例如swap(string, string)总是没有string.swap(string)快速。又比如sort无法对list进行排序,这时候只能使用list.sort()来给链表排序。这时候就需要使用一定的方法将对象内部的函数“变成”函数对象,这样的函数对象叫做成员函数适配器,其实前面的binder也是一种适配器。看下面的例子:
|
上面的例子中,遍历vector<list<int> >并对链表进行排序。其中使用的是成员函数适配器mem_fun_ref,它返回的函数对象会以list<int>对象的引用为参数。另外一个mem_fun则是以指向list<int>对象的指针为参数。