转发碎片备份

与 Boost.Function 一起使用 Boost.Lambda

与 Boost.Function 兼容于由 Boost.Bind 创建的函数对象一样,它也支持由 Boost.Lambda 创建的函数对象。你用 Lambda 库创建的任何函数对象都兼容于相应的 boost::function. 我们在前一节已经讨论了基于绑定的一些内容,使用 Boost.Lambda 的主要不同之处是它能做得更多。我们可以轻易地创建一些小的、无名的函数,并把它们保存在 boost::function 实例中以用于后续的调用。我们已经在前一章中讨论了 lambda 表达式,在那一章的所有例子中所创建的函数对象都可以保存在一个 function 实例中。function 与创建函数对象的库的结合使用会非常强大。

代价的考虑

有一句谚语说,世界上没有免费的午餐,对于 Boost.Function 来说也是如此。与使用函数指针相比,使用 Boost.Function 也有一些缺点,特别是对象大小的增加。显然,一个函数指针只占用一个函数指针的空间大小(这当然了!),而一个 boost::function实例占的空间有三倍大。如果需要大量的回调函数,这可能会成为一个问题。函数指针在调用时的效率也稍高一些,因为函数指针是被直接调用的,而 Boost.Function 可能需要使用两次函数指针的调用。最后,可能在某些需要与C库保持后向兼容的情形下,只能使用函数指针。

虽然 Boost.Function 可能存在这些缺点,但是通常它们都不是什么实际问题。额外增加的大小非常小,而且(可能存在的)额外的函数指针调用所带来的代价与真正执行目标函数所花费的时间相比通常都是非常小的。要求使用函数而不能使用 Boost.Function 的情形非常罕见。使用这个库所带来的巨大优点及灵活性显然超出这些代价。

幕后的细节

至少了解一下这个库如何工作的基础知识是非常值得的。我们来看一下保存并调用一个函数指针、一个成员函数指针和一个函数对象这三种情形。这三种情形是不同的。要真正看到 Boost.Function 如何工作,只有看源代码——不过我们的做法有些不同,我们试着搞清楚这些不同的版本究竟在处理方法上有些什么不同。我们也有一个不同要求的类,即当调用一个成员函数时,必须传递一个实例的指针给 function1 (这是我们的类的名字)的构造函数。function1 支持只有一个参数的函数。与 Boost.Function 相比一个较为宽松的投条件是,即使是对于成员函数,也只需要提供返回类型和参数类型。这个要求的直接结果就是,构造函数必须被传入一个类的实例用于成员函数的调用(类型可以自动推断)。

我们将要采用的方法是,创建一个泛型基类,它声明了一个虚拟的调用操作符函数;然后,从这个基类派生三个类,分别支持三种不同形式的函数调用。这些类负责所有的工作,而另一个类,function1, 依据其构造函数的参数来决定实例化哪一个具体类。以下是调用器的基类,invoker_base.

template <typename R, typename Arg> class invoker_base {
public:
virtual R operator()(Arg arg)=0;
};

接着,我们开始定义 function_ptr_invoker, 它是一个具体调用器,公有派生自 invoker_base. 它的目的是调用普通函数。这个类也接受两个类型,即返回类型和参数类型,它们被用于构造函数,构造函数接受一个函数指针作为参数。

template <typename R, typename Arg> class function_ptr_invoker 
: public invoker_base<R,Arg> {
R (*func_)(Arg);
public:
function_ptr_invoker(R (*func)(Arg)):func_(func) {}

R operator()(Arg arg) {
return (func_)(arg);
}
};

这个类模板可用于调用任意一个接受一个参数的普通函数。调用操作符简单地以给定的参数调用保存在 func_ 中的函数。请注意(的确有些奇怪)声明一个保存函数指针的变量的那行代码。

R (*func_)(Arg);

你也可以用一个 typedef 来让它好读一些。

typedef R (*FunctionT)(Arg);
FunctionT func_;

接着,我们需要一个可以处理成员函数调用的类模板。记住,它要求在构造时给出一个类实例的指针,这一点与 Boost.Function 的做法不一样。这样可以节省我们的打字,因为是编译器而不是程序员来推导这个类。

template <typename R, typename Arg, typename T> 
class member_ptr_invoker :
public invoker_base<R,Arg> {
R (T::*func_)(Arg);
T* t_;
public:
member_ptr_invoker(R (T::*func)(Arg),T* t)
:func_(func),t_(t) {}

R operator()(Arg arg) {
return (t_->*func_)(arg);
}
};

这个类模板与普通函数指针的那个版本很相似。它与前一个版本的不同在于,构造函数保存了一个成员函数指针与一个对象指针,而调用操作符则在该对象(t_)上调用该成员函数(func_)。

最后,我们需要一个兼容函数对象的版本。这是所有实现中最容易的一个,至少在我们的方法中是这样。通过使用单个模板参数,我们只表明类型 T 必须是一个真正的函数对象,因为我们想要调用它。说得够多了。

template <typename R, typename Arg, typename T> 
class function_object_invoker :
public invoker_base<R,Arg> {
T t_;
public:
function_object_invoker(T t):t_(t) {}

R operator()(Arg arg) {
return t_(arg);
}
};

现在我们已经有了这些适用的积木,剩下来的就是把它们放在一起组成我们的自己的 boost::function, 即 function1 类。我们想要一种办法来发现要实例化哪一个调用器。然后我们可以把它存入一个 invoker_base 指针。这里的窃门就是,提供一些构造函数,它们有能力去检查对于给出的参数,哪种调用器是正确的。这仅仅是重载而已,用了一点点手法,包括泛化两个构造函数。

template <typename R, typename Arg> class function1 {
invoker_base<R,Arg>* invoker_;
public:
function1(R (*func)(Arg)) :
invoker_(new function_ptr_invoker<R,Arg>(func)) {}

template <typename T> function1(R (T::*func)(Arg),T* p) :
invoker_(new member_ptr_invoker<R,Arg,T>(func,p)) {}

template <typename T> function1(T t) :
invoker_(new function_object_invoker<R,Arg,T>(t)) {}

R operator()(Arg arg) {
return (*invoker_)(arg);
}

~function1() {
delete invoker_;
}
};

如你所见,这里面最难的部分是正确地定义出推导系统以支持函数指针、类成员函数以及函数对象。无论使用何种设计来实现这类功能的库,这都是必须的。最后,给出一些例子来测试我们这个方案。

bool some_function(const std::string& s) {
std::cout << s << " This is really neat\n";
return true;
}

class some_class {
public:
bool some_function(const std::string& s) {
std::cout << s << " This is also quite nice\n";
return true;
}
};

class some_function_object {
public:
bool operator()(const std::string& s) {
std::cout << s <<
" This should work, too, in a flexible solution\n";
return true;
}
};

我们的 function1 类可以接受以下所有函数。

int main() {
function1<bool,const std::string&> f1(&some_function);
f1(std::string("Hello"));

some_class s;
function1<bool,const std::string&>
f2(&some_class::some_function,&s);

f2(std::string("Hello"));

function1<bool,const std::string&>
f3(boost::bind(&some_class::some_function,&s,_1));

f3(std::string("Hello"));

some_function_object fso;
function1<bool,const std::string&>
f4(fso);
f4(std::string("Hello"));
}

它也可以使用象 Boost.Bind 和 Boost.Lambda 这样的 binder 库所返回的函数对象。我们的类与 Boost.Function 中的类相比要简单多了,但是也已经足以看出创建和使用这样一个库的问题以及相关解决方法。知道一点关于一个库是如何实现的事情,对于有效使用这个库是非常有用的。




如果没有boost::bind,那么boost::function就什么都不是,而有了bind(),“同一个类的不同对象可以delegate给不同的实现,从而实现不同的行为”(myan语),简直就无敌了。

    C++通过范型编程倒是映入了大量函数编程的思想和手法,但终究强类型的语言,有时代码就不是那么优雅了.
    一般情况下函数指针和函数对象(functor)是可以互换的,如std::for_each的第三个参数,但标准库中有4个函数配接器 (not1,not2,bind1st,bind2nd)是不能接受函数指针,必须使用std::ptr_fun将函数指针封装成函数对象.还有一种情况 是函数指针指向的函数的参数类型与函数模板需要的不匹配,如果要通过for_each调用每个Iterator的解引用(dereference)的对象 的某个成员函数.直接使用诸如ClassA::*member_func的成员函数指针显然是想当然的做法,调用的语法都不一致,结果是一大堆编译错误信 息.没有关系可以使用std::mem_fun和std::mem_fun_ref.如 for_each(classAVector.begin(),classAVector.end(),mem_fun_ref(ClassA::*member_func)). 标准库中的这一套函数适配器(functor adapters)使用起来要充分考虑使用的场合.不同的情况使用不同配接器.特别是您要区分for_each的容器是 Container<T>还是Container<T*>,前者使用std::mem_fun_ref,后者使用 std::mem_fun.这一段的详细讨论可一参照Effective STL的第40条和第41条.(个人感觉这几个配接器的名字真有的...fun).
    如果您实在是有点受不了或者你不能确定容器中究竟放的是什么而且你愿意使用一下准标准库,就可以考虑boost库的 mem_fn.boost::mem_fn也不是很特别的boost成员.使用boost::mem_fn好处:不用关心容器中是T还是T*甚至是 boost::share_ptr<T>,另外买一赠一fn比fun更好听一点.下面抄录boost的帮助代码就很说明问题了.
struct X
{
    
void f();
};

void g(std::vector<X> & v)
{
     std::for_each(v.begin(), v.end(), boost::mem_fn(
&X::f));
};

void h(std::vector<*> const & v)
{
     std::for_each(v.begin(), v.end(), boost::mem_fn(
&X::f));
};

void k(std::vector<boost::shared_ptr<X> > const & v)
{
     std::for_each(v.begin(), v.end(), boost::mem_fn(
&X::f));
};

for_each(m_primFolder.begin(),m_primFolder.end(),boost::bind(boost::mem_fn(&HASH_PATH::UpLoadRes),_1,m_fileManager));

for_each(lis.begin(),lis.end(),boost::bind(boost::mem_fn(&CFolderInfo::WriteFileXml),this,_1,boost::ref(filelist)));

for_each(m_fileList.begin(),m_fileList.end(),boost::bind(boost::mem_fn(&CFolderInfo::AddRes),this,_1,thread,folder));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值