boost::mem_fn和std::mem_fun在MSVC6.0上的表现
Article last modified on 2002-8-7
----------------------------------------------------------------
The information in this article applies to:
- C/C++
- Microsoft Visual C++ 6.0(SP5)
----------------------------------------------------------------
boost::mem_fn的简单介绍:
boost::mem_fn是std::mem_fun系列的一个扩展。它的文档链接为:
http://www.boost.org/libs/bind/mem_fn.html。
mem_fn最为人所熟知的作用是,将一个成员函数作用在一个容器上,就像这样std::for_each(v.begin(), v.end(), boost::mem_fn(&Shape::draw))就可以让容器vector中的每一个元素都执行一遍draw方法。
第二个用法是,它可以帮助把一个函数指针模拟得像一个函数实体(function object)。比如:
template<class It, class R, class T> void for_each(
It first,
It last,
R (T::*pmf) () )
{
std::for_each(first, last, boost::mem_fn(pmf) );
}
这样,你就可以这么调用:
for_each(v.begin(), v.end(), &Shape::draw);
但是仅就这些功能,是不足以让开发者舍弃std::mem_fun[_ref]。有什么理由吗?
std::mem_fun[_ref]在MSVC6.0中的编译错误:
第一个问题:std::mem_fun为什么非要成员函数有返回值呢?
试验代码:
class Stuff
{
public:
std::string m_Name;
// This function works under VC6, because it returns a value from the function.
// Note: there is nothing special about returning an int; it could be any data
// type.
int printName()
{
std::cout << m_Name << std::endl;
return 0;
}
// This function would not work under VC6, although Josuttis provides
// a similar example.
// void printName()
// {
// std::cout << m_Name << std::endl;
// }
};
void main()
{
std::vector<Stuff> v;
Stuff s1;
s1.m_Name = "Brandon";
v.push_back(s1);
std::for_each(v.begin(), v.end(), std::mem_fun_ref(&Stuff::printName));
}
编译结果:
只有把printname成员函数声明为有返回值的形式,才能编译通过;
如果声明为void printname(),则会出现下面的编译错误:
f:\program files\microsoft visual studio\vc98\include\functional(263) : error C2562: '()' : 'void' function returning a value
f:\program files\microsoft visual studio\vc98\include\functional(262) : see declaration of '()'
f:\program files\microsoft visual studio\vc98\include\functional(263) : while compiling class-template member function 'void __thiscall std::mem_fun_ref_t<void,class Stuff>::operator ()(class Stuff &) const'
原来是因为MSVC的stl的实现为:
template<class _R, class _Ty>
class mem_fun_ref_t : public unary_function<_Ty, _R>
{
………….
_R operator()(_Ty& _X) const
{return ((_X.*_Ptr)()); }
}
也就是说,operator()非要将成员函数的返回值作为它的返回值,而不管这个成员函数是否有返回值。
暂且不管这种做法是否有道理,它确实会带来不便。
第二个问题:std::mem_fun最多只能支持成员函数有一个参数
试验代码:
class StoreVals
{
int val;
public:
StoreVals ( ) { val = 0; }
int lessconst ( int k , int m) {val -= k + m; return val; }
};
int main(int argc, char* argv[])
{
std::vector <StoreVals* > v2;
std::for_each(
v2.begin( ),
v2.end( ),
std::bind2nd (
std::mem_fun1<int, StoreVals,int>
( &StoreVals::lessconst ),
5
)
);
return 0;
}
std::mem_fun和std::mem_fun_ref支持的成员函数没有参数,而std::mem_fun1和std::mem_fun1_ref支持的成员函数只有一个参数,然后就到此为止了。
如果我们有这样一个函数int lessconst ( int k , int m) {val -= k + m; return val; },就没办法利用std::mem_fun了。
第三个问题:std::mem_fun不支持智能指针
std::mem_fun只能处理raw pointer,而无法处理smart pointer。
第四个问题:std::mem_fun不支持__stdcall的调用约定
试验代码:
class Stuff
{
public:
std::string m_Name;
int __stdcall printName()
{
std::cout << m_Name << std::endl;
return 0;
}
};
void main()
{
std::vector<Stuff> v;
Stuff s1;
s1.m_Name = "Brandon";
v.push_back(s1);
std::for_each(v.begin(), v.end(), std::mem_fun_ref(&Stuff::printName));
}
编译结果:
error C2664: 'mem_fun_ref' : cannot convert parameter 1 from 'int (__stdcall Stuff::*)(void)' to 'int (__thiscall Stuff::*)(void)'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
这样,你就无法让Windows API functions,COM interface member functions和std::mem_fun一起使用了。
第五个问题:有std::mem_fun和std::mem_fun_ref,还有 std::mem_fun1、std::mem_fun1_ref等等,使用起来复杂
怎么样?看到std::mem_fun[_ref]的局限性了吧。下面我们来看看boost:;mem_fn[_ref]是怎样改进的。
boost::mem_fn[_ref]的改进:
第一个问题:boost::mem_fn允许成员函数没有返回值
试验代码:
class Stuff
{
public:
std::string m_Name;
void printName()
{
std::cout << m_Name << std::endl;
}
};
void main()
{
std::vector<Stuff> v;
Stuff s1;
s1.m_Name = "Brandon";
v.push_back(s1);
std::for_each(v.begin(), v.end(), boost::mem_fn(&Stuff::printName));
}
编译通过。
第二个问题:boost::mem_fn最多只能支持成员函数有8个参数
试验代码:
mem_fn(&X::g8)(sp, 1, 2, 3, 4, 5, 6, 7, 8);
如果还要把一个多参数成员函数作用于一个容器的话,好像就用不了boost::mem_fn了。我试了试,只有下面这种不是成员函数的情况能编译通过。
class StoreVals
{
int val;
public:
StoreVals ( ) { val = 0; }
friend int lessconst( StoreVals* _sto, int k , int m);
};
int lessconst( StoreVals* _psto, int k , int m)
{
_psto->val -= k + m;
return _psto->val;
}
void main()
{
std::vector <StoreVals* > v2;
std::for_each(
v2.begin( ),
v2.end( ),
boost::bind(
lessconst , _1, 5, 7
)
);
}
如果把lessconst声明为成员函数,就编译不过去了,如下所示:
class StoreVals
{
int val;
public:
StoreVals ( ) { val = 0; }
int lessconst1 ( StoreVals* _psto, int k , int m)
{
_psto->val -= k + m;
return _psto->val;
}
}
void main()
{
std::vector <StoreVals* > v2;
std::for_each(
v2.begin( ),
v2.end( ),
boost::bind(
&StoreVals::lessconst1 , _1, 5, 7
)
);
}
出来一大堆莫名其妙的错误(57个),像
error C2780: 'class boost::_bi::bind_t<R,class boost::_mfi::dm<R,T>,class boost::_bi::list1<class boost::_bi::value<R> > > __cdecl boost::bind(R T::*,A1)' : expects 2 arguments - 4 provided
之流的。不知道为什么。
第三个问题:boost::mem_fn支持智能指针
第四个问题:boost::mem_fn支持__stdcall的调用约定
要想让boost::mem_fn支持__stdcall调用约定,必须在
#include <boost/bind.hpp>
#include <boost/mem_fn.hpp>
之前定义一个宏:
#define BOOST_MEM_FN_ENABLE_STDCALL
。
请注意,必须在这两个头文件include之前定义宏,否则仍然会有这样的编译错误:
error C2665: 'mem_fn' : none of the 2 overloads can convert parameter 1 from type 'int (__stdcall Stuff::*)(void)'
第五个问题:无论什么情况,只用一个boost:mem_fn即可
所以,你可以这样,也可以那样,但是请记住boost::mem_fn吧。
本文档所包含的信息代表了在发布之日,ZhengYun 对所讨论问题的当前看法,Zhengyun 不保证所给信息在发布之日以后的准确性。
本文档仅供参考。对本文档中的信息,Zhengyun 不做任何明示或默示的保证。
Written by zhengyun@tomosoft.com