了解使用ptr_fun、mem_fun和mem_fun_ref的原因1(Effective stl 条款41)

来源:互联网 发布:falcon python 编辑:程序博客网 时间:2024/06/05 10:31
 
如果我有一个函数f和一个对象x,我希望在x上调用f,而且我在x的成员函数之外。C++给我三种不同的语法来实现这个调用:
 
f(x);    // 语法#1:当f是一个非成员函数
x.f();  // 语法#2:当f是一个成员函数,而且x是一个对象或一个对象的引用
p->f(); // 语法#3:当f是一个成员函数,而且p是一个对象的指针
 
 
现在,假设我有一个可以测试Widget的函数:
 
 void test(Widget& w);// 测试w,如果没通过就标记为“failed”
 
 
要测试容器vw中的每个Widget,我很显然可以这么使用for_each
 vector<Widget> vw; // vw容纳Widget
 for_each(vw.begin(), vw.end(), test);// 调用#1(可以编译)
 
 
但想象test是一个Widget的成员函数而不是一个非成员函数,也就是说,Widget支持自我测试:
 
class Widget {
public
:
     
void test(); // 进行自我测试;如果没通过就把*this标记为“failed”

}
;
这样调用widgettest函数:
 vector<Widget> vw; // vw容纳Widget
 for_each(vw.begin(), vw.end(),&Widget::test);// 调用#2(不能编译)
 
 
同样,对一个指针容器调用for_each
 
list<Widget*> lpw;// lpw容纳Widget的指针
for_each(lpw.begin(), lpw.end(),&Widget::test);//调用#3(也不能编译)
 
 
 
上面这两种情况都不能通过编译,为什么呢?还是看for_each源码吧:
 
// TEMPLATE FUNCTION for_each
template<class _InIt,class _Fn1> 
inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
{    // perform function for each element
     _DEBUG_RANGE(_First, _Last);
     _DEBUG_POINTER(_Func);
     _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
     _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
     
for (; _ChkFirst != _ChkLast; ++
_ChkFirst)
         _Func(
*_ChkFirst); //原来for_each函数默认用的是语法#1调用函
                             //数 return (_Func);
}

 
 
所以,上面两种调用不能通过编译就不奇怪了,因为for_each函数内部默认以非成员函数调用方式(语法#1)调用,而我们确传递了一个成员函数进来,当然编译不过了,除非这样重写两个for_each函数,一个适合语法#2对象调用版本,另一个适合语法#3对象指针调用版本 
 
1.语法#2版本
template
<class _InIt,class _Fn1>
 
inline _Fn1 for_each_ref(_InIt _First, _InIt _Last, _Fn1 _Func)
{    // perform function for each element
     _DEBUG_RANGE(_First, _Last);
     _DEBUG_POINTER(_Func);
     _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
     _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
     
for (; _ChkFirst != _ChkLast; ++
_ChkFirst)
         ((
*_ChkFirst).*_Func)();   //语法#2对象调用的for_each版本

     return (_Func);
}
2.语法#3版本
template<class _InIt,class _Fn1>
inline _Fn1 for_each_ptr(_InIt _First, _InIt _Last, _Fn1 _Func)
{    // perform function for each element
     _DEBUG_RANGE(_First, _Last);
     _DEBUG_POINTER(_Func);
     _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
     _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
     for (; _ChkFirst != _ChkLast; ++_ChkFirst)
         ((*_ChkFirst)->*_Func)(); //语法#3对象指针调用的for_each版本
     return (_Func);
}
 
 
我们这两个版本确实能够工作,但是,除了重写for_each就没其他办法了吗?当然不是,stl已经提供了两个函数来解决这个问题,那就是mem_funmem_fun_ref
 
list<Widget> lpw; 
for_each(lpw.begin(), lpw.end(),mem_fun_ref(
&Widget::test));//OK,语法#2调用

list<Widget*> lpw; //list的元素类型为指针
for_each(lpw.begin(), lpw.end(),mem_fun(&Widget::test)); //现在可以编译了
 
 
通过mem_funmem_fun_ref转化之后,现在可以在for_each函数上调用类的成员函数了。其实mem_funmem_fun_ref都返回了一个仿函数对象,这个仿函数对象包装了相应成员函数,这个仿函数能满足for_each函数的调用条件,而实际的成员函数调用却是在仿函数对象内部调用。关于mem_funmem_fun_ref的工作原理下回再来
原创粉丝点击