如果我有一个函数f和一个对象x,我希望在x上调用f,而且我在x的成员函数之外。C++给我三种不同的语法来实现这个调用:
f(x); // 语法#1:当f是一个非成员函数
x.f(); // 语法#2:当f是一个成员函数,而且x是一个对象或一个对象的引用
p->f(); // 语法#3:当f是一个成员函数,而且p是一个对象的指针
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”
} ;
public:
void test(); // 进行自我测试;如果没通过就把*this标记为“failed”
} ;
这样调用widget的test函数:
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(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调用函
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);
}
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_fun和mem_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)); //现在可以编译了
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)); //现在可以编译了