仿函数(functor): 重载了"()"运算符的struct,例如:
struct D {
D(int i=0){num=i;}
int num;
};
struct print_D{
void operator()(const D* d)const{
cout<<"I am D. my num="<<d->num<<endl;
}
};
int main()
{
vector<D*> V;
V.push_back(new D(1));
V.push_back(new D(2));
V.push_back(new D);
V.push_back(new D(3));
for_each(V.begin(), V.end(), print_D());
}
编译输出:
I am D. my num=1
I am D. my num=2
I am D. my num=0
I am D. my num=3
如果使用mem_fun,会方便很多:
struct D {
D(int i=0){num=i;}
void print() { cout << "I'm a D. my num=" << num<< endl; }
int num;
};
int main()
{
vector<D*> V;
V.push_back(new D(1));
V.push_back(new D(2));
V.push_back(new D);
V.push_back(new D(3));
for_each(V.begin(), V.end(), mem_fun(&D::print));
}
那看看源码是怎么回事,在SGI STL的stl_function.h:
template <class _Ret, class _Tp>
inline mem_fun_t<_Ret,_Tp> mem_fun(_Ret (_Tp::*__f)())
{ return mem_fun_t<_Ret,_Tp>(__f); }
原来mem_fun返回的是一个对象:mem_fun_t<_Ret,_Tp>.(不要嫌人家命名太怪异).那mem_fun_t<_Ret,_Tp>又是什么东东?还是看源码:
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_t就是一个functor,这下就满足了for_each的要求了。其调用流程是这样的,for_each把vector中的元素传送给mem_fun,mem_fun自己产生一个仿函数mem_fun_t,然后仿函数调用其重载的()。过程就这么简单。当然你不能对其他类的成员函数进行绑定,因为在for_each调用过程中,会传递其*iterator值,如果是其他类的成员函数,那么这个类的对象无法传入,当然就无法完成任务了。
这里使用的是vector<D*> V; 在mem_fun_t构造函数中,刚好需要指针,如果不是D*, 而是使用vector<D> V; 还能用吗?
这是你需要使用的是mem_fun_ref。把程序改成:
struct D {
D(int i=0){num=i;}
void print() { cout << "I'm a D. my num=" << num<< endl; }
int num;
};
int main()
{
vector<D> V;
V.push_back(D(1));
V.push_back( D(2));
V.push_back( D());
V.push_back( D(3));
for_each(V.begin(), V.end(), mem_fun_ref(&D::print));
}
mem_fun对于一些多态的虚函数也十分有用,注意看下面的例子:
struct B {
virtual void print() = 0;
};
struct D1 : public B {
void print() { cout << "I'm a D1" << endl; }
};
struct D2 : public B {
void print() { cout << "I'm a D2" << endl; }
};
int main()
{
vector<B*> V;
V.push_back(new D1);
V.push_back(new D2);
V.push_back(new D2);
V.push_back(new D1);
for_each(V.begin(), V.end(), mem_fun(&B::print));
}
mem_fun1和mem_fun1_ref,他们可以使用绑定一个参数的类成员。
Code
template<class _Result,
class _Ty> inline
mem_fun_t<_Result, _Ty> mem_fun(_Result (_Ty::*_Pm)())
{ // return a mem_fun_t functor adapter
return (std::mem_fun_t<_Result, _Ty>(_Pm));
}
template<class _Result,
class _Ty,
class _Arg> inline
mem_fun1_t<_Result, _Ty, _Arg> mem_fun(_Result (_Ty::*_Pm)(_Arg))
{ // return a mem_fun1_t functor adapter
return (std::mem_fun1_t<_Result, _Ty, _Arg>(_Pm));
}
测试代码如下:
#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <string>
using namespace std;
class Book
{
public:
string m_strName;
Book(string name) : m_strName(name)
{
}
void SayHello()
{
cout << "Hello " << m_strName << endl;
}
};
class CBookEditDlg
{
public:
vector<Book> m_vecBooks;
void ForEachBookFunctor(Book book);
void PubBookInfo();
};
void CBookEditDlg::ForEachBookFunctor(Book book)
{
cout << book.m_strName << endl;
}
void CBookEditDlg::PubBookInfo()
{
//下面这两句起的作用是一样的,至于为什么一样很值得仔细揣摩揣摩的
for_each(m_vecBooks.begin(), m_vecBooks.end(), mem_fun_ref(&Book::SayHello));
for_each(m_vecBooks.begin(), m_vecBooks.end(), std::bind1st(mem_fun(&CBookEditDlg::ForEachBookFunctor), this));
}
int main()
{
Book book1("Effective C++");
Book book2("More Effective C++");
CBookEditDlg books;
books.m_vecBooks.push_back(book1);
books.m_vecBooks.push_back(book2);
books.PubBookInfo();
return 0;
}
先理一下关系吧,bind1st返回一个binder1st对象,而binder1st构造函数需要接受两个参数,分别为mem_fun_t或者是mem_fun1_t对象和对象this指针,mem_fun_t和mem_fun1_t则用一个函数指针保存有对象成员方法的地址。既然这里用到了mem_fun1_t,我们就来分析它的源代码。
// TEMPLATE CLASS mem_fun1_t
template<class _Result,
class _Ty,
class _Arg>
class mem_fun1_t
: public binary_function<_Ty *, _Arg, _Result>
{ // functor adapter (*p->*pfunc)(val), non-const *pfunc
public:
explicit mem_fun1_t(_Result (_Ty::*_Pm)(_Arg))
: _Pmemfun(_Pm)
{ // construct from pointer
}
_Result operator()(_Ty *_Pleft, _Arg _Right) const
{ // call function with operand
return ((_Pleft->*_Pmemfun)(_Right));
}
private:
_Result (_Ty::*_Pmemfun)(_Arg); // the member function pointer
};
上面的测试代码中,binder1st接受的_Func类型为std::mem_fun1_t<void,CBookEditDlg,Book>,这个可以通Local窗口看到的,其父类binary_function为std::binary_function<CBookEditDlg *,Book,void>,这样可以很清楚看到_Result对应void,_Ty对应CBookEditDlg,_Arg对应Book,进一步也就是说binary_function三个typedef的类型中,first_argument_type为CBookEditDlg *,second_argument_type为Book,result_type为void,可以看出这里必须使用bind1st绑定器将this绑定在第一个参数上,bind2nd同bind1st是非常类似的,如果这里误用了bind2nd将通不过编译(最后面我们给出一个例子看看bind1st和bind2nd都可以用的情况)。我们现在可以很清楚理解mem_fun1_t了,它里面有一个函数指针,定义为"void (CBookEditDlg::*_Pmemfun)(Book)"。mem_fun1_t重载了函数运算符,它接受两个参数,分别为对象的this指针和参数_Right。我现在真的是非常佩服C++编译器的能力,bind1st,mem_fun我都没有给出实例化参数,它能从提供的参数自动提取出型别来实例化模板。例子程序中它就知道使用那个一个mem_fun模板,我之前一直以为它会返回一个mem_fun_t对象,然后就怎么都想不通了。
ok,接下来我们看binder1st的代码。
// TEMPLATE CLASS binder1st
template<class _Fn2>
class binder1st
: public unary_function<typename _Fn2::second_argument_type,
typename _Fn2::result_type>
{ // functor adapter _Func(stored, right)
public:
typedef unary_function<typename _Fn2::second_argument_type,
typename _Fn2::result_type> _Base;
typedef typename _Base::argument_type argument_type;
typedef typename _Base::result_type result_type;
binder1st(const _Fn2& _Func,
const typename _Fn2::first_argument_type& _Left)
: op(_Func), value(_Left)
{ // construct from functor and left operand
}
result_type operator()(const argument_type& _Right) const
{ // apply functor to operands
return (op(value, _Right));
}
result_type operator()(argument_type& _Right) const
{ // apply functor to operands
return (op(value, _Right));
}
protected:
_Fn2 op; // the functor to apply
typename _Fn2::first_argument_type value; // the left operand
};
binder1st有一个op保存之前的那个mem_fun1_t对象,然后它重载了函数运算符,接受两个参数,一个是value,一个是_Right,value是CBookEditDlg *类型的,它就是bind1st接受的this参数了,_Right则是Book的对象引用。这里之所以是引用是因为,for_each传递给它后面函数对象并不是迭代器,而是*迭代器,在例子程序中就是Book对象了。有人可能认为例子程序中ForEachBookFunctor函数如果接受Book引用效率会更高一些,但是如果真将"Book book"改成"Book& book"会导致通不过编译,这是因为binary_function的second_argument_type就变成了"Book&"了,这个时候"result_type operator()(argument_type& _Right) const"就出现了对引用的引用了,所以通不过编译。我想STL大量的拷贝是降低其效率的一个重要因素。分析binder1st我们就能明白binary_function所取到的重要作用,有了它binder1st重载函数运算符的那个函数才知道自己应该接受什么类型的参数,返回什么类型的参数。
op(value, _Right)会调用下面这个函数(mem_fun1_t重载的):
_Result operator()(_Ty *_Pleft, _Arg _Right) const
{ // call function with operand
return ((_Pleft->*_Pmemfun)(_Right));
}
最后附上一段有趣的代码(引自http://cpp-circle.group.javaeye.com/group/blog/336258,这里都只是讲一元函数和二元函数而已,如果这个sum做三个参数之和,我想绑定器就要傻眼了)
#include <functional>
#include <iostream>
using namespace std;
int sum(int arg1, int arg2)
{
cout<<"ARG 1: "<<arg1<<endl;
cout<<"ARG 2: "<<arg2<<endl;
int sum = arg1 + arg2;
cout<<"SUM: "<<sum<<endl;
return sum;
}
int main(int argc,char* argv[])
{
bind1st(ptr_fun(sum),1)(2); // the same as sum(1,2)
cout << "===========================" << endl;
bind2nd(ptr_fun(sum),1)(2); //the same as sum(2,1)
return 0;
}