首先我们看个例子。
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
using namespace std;
class Widget{
public:
Widget(int a) : m_a(a)
{
}
int value() const
{
return m_a;
}
bool test()
{
return m_a % 3;
}
private:
int m_a;
};
bool test(const Widget& w)
{
return w.value() % 3;
}
int main()
{
typedef vector<Widget> WdgVec;
typedef vector<Widget*> WdgPtrVec;
typedef vector<Widget>::iterator WdgIter;
typedef vector<Widget*>::iterator WdgPtrIter;
WdgVec vw;
WdgPtrVec vwp;
for(int index = 0; index < 10; ++index)
{
Widget w(index);
vw.push_back(w);
vwp.push_back(new Widget(index));
}
for_each(vw.begin(), vw.end(), test);
for_each(vw.begin(), vw.end(), &Widget::test);
for_each(vwp.begin(), vwp.end(), &Widget::test);
return 0;
}
通过编译运行上面的例子,我们能够得知,其中:
for_each(vw.begin(), vw.end(), &Widget::test);
for_each(vwp.begin(), vwp.end(), &Widget::test);
这两行代码都是编译不通过的。编译报错:
[Error] must use '.*' or '->*' to call pointer-to-member function in '__f (...)', e.g. '(... ->* __f) (...)'
为什么呢?
按照正常理解,我们传给了for_each
算法一个函数地址,应该是能够正常编译通过的。
如果有一个函数f()
和一个对象x
,假设我们要在对象x
上调用函数,通常是有三种语法来进行这个调用。
- 如果函数
f()
不是对象x的成员函数,那么我们直接调用f(x)
- 如果函数
f()
是对象x的成员函数,并且x
是一个对象或者一个对象的引用,则x.f()
- 如果函数
f()
是对象x的成员函数,并且p
是一个指向对象x的指针,则p->f()
在上面的例子中,我们创建了两个容器,vw
和vwp
,一个存放对象Widget
,一个存放指向对象Widget
的指针。
当函数test()
不是对象Widget
的成员函数时,我们直接调用了函数。并且编译正常。那么在理想情况下,当函数test()是对象的成员函数时,我们应该也是能够通过&Widget::test
来调用它。
其实,在上面的三次调用中,分别对应了我们前面提到的C++提供的三种语法的调用方式。但是for_each
算法是只有一个的,我们看下for_each
的实现。
template <class _InputIter, class _Function>
_Function for_each(_InputIter __first, _InputIter __last, _Function __f) {
__STL_REQUIRES(_InputIter, _InputIterator);
for ( ; __first != __last; ++__first)
__f(*__first);
return __f;
}
通过上面for_each
的实现我们可以很明显的看出来,for_each
的实现是基于我们前面所说的语法一来进行对函数调用的。这也是stl中一种普遍的惯例:函数或者函数对象被调用时,总是使用非成员函数的语法形式。
这也就证明了上面我们的后两次调用为什么编译不通过的。
至此,或许mem_fun
和mem_fun_ref
必须存在的意义已经很明显了,也就是他们用来调整(后两种调用方式)成员函数,能够被通过非成员函数的方式调用。
mem_fun
和mem_fun_ref
是函数模版,实现方式有多种,我们看一下其中一种。
template <class _Ret, class _Tp>
inline mem_fun_t<_Ret,_Tp> mem_fun(_Ret (_Tp::*__f)())
{ return mem_fun_t<_Ret,_Tp>(__f); }
template <class _Ret, class _Tp>
class mem_fun_t : public unary_function<_Tp*,_Ret> {
public:
explicit mem_fun_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {}
_Ret operator()(_Tp* __p) const { return (__p->*_M_f)(); }
private:
_Ret (_Tp::*_M_f)();
};
mem_fun(_Ret (_Tp::*__f)())
带一个指向某个成员函数的指针参数__f
,并且返回一个 mem_fun_t
的对象。
而 mem_fun_t
是一个函数子类,他拥有该成员函数的指针,并提供了operator()
函数,在operator()
中调用了通过参数传递进来的对象的成员函数。
所以我们可以修改前面例子中的代码。
for_each(vw.begin(), vw.end(), mem_fun_ref(&Widget::test));
for_each(vwp.begin(), vwp.end(), mem_fun(&Widget::test));
来正常的调用成员函数。
我们的第一个调用。
for_each(vw.begin(), vw.end(), test);
传给for_each
的是一个真正的函数,所以,我们就不必要对for_each
进行语法调整。
前面我们也提到了ptr_fun
,下面我们先看下ptr_fun
的实现。
template <class _Arg, class _Result>
inline pointer_to_unary_function<_Arg, _Result> ptr_fun(_Result (*__x)(_Arg))
{
return pointer_to_unary_function<_Arg, _Result>(__x);
}
如果你已经度过前面的文章的话,你或许应该已经知道了,ptr_fun
提供了给函数加上类型定义的功能。
比如在stl中的四个标准的配接器中,必须要使用类型定义。
比如我们前面的调用
for_each(vw.begin(), vw.end(), test);
但是如果当我们使用下面的调用的时候,就需要使用ptr_fun来进行类型定义的设置。
for_each(vw.begin(), vw.end(), not1(test));
其实很多时候如果你并不知道该不该加ptr_fun
的时候,加上总是没有错的,也不会影响算法的性能,唯一的不好只是在别人阅读你的代码的时候会造成一些障碍。
而也可以在任何地方都不加,当编译器报错提醒你的时候再返回来加进去就行。
但是mem_fun
和mem_fun_ref
是当调用成员函数的时候必须要加的,因为在调用的时候,并不是只引入了类型定义,而且还进行了语法调用形式的转换来适应算法。