热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Boost学习系列3-函数对象(上)

这次主要介绍的是函数对象,可能称为“高阶函数”更为适合。它实际上是指那些可以被传入到其它函数或是从其它函数返回的一类函数,在C++中高阶函数是被实现为函数对象的。本文将会介绍几个用于处理
    这次主要介绍的是函数对象,可能称为“高阶函数”更为适合。它实际上是指那些可以被传入到其它函数或是从其它函数返回的一类函数,在C++中高阶函数是被实现为函数对象的。本文将会介绍几个用于处理函数对象的Boost C++库。 其中, Boost.Bind 可替换来自C++标准的著名的 std::bind1st() 和 std::bind2nd() 函数,而 Boost.Function 则提供了一个用于封装函数指针的类。 最后, Boost.Lambda 则引入了一种创建匿名函数的方法。

一、Boost.Bind

    Boost.Bind简化了由C++标准中的std::bind1st和std::bind2nd 模板函数所提供的一个机制:将这些函数与几乎不限数量的参数一起使用,就可以得到指定签名的函数。这种情形的一个最好的例子就是在C++标准中定义的多个不同算法。

#include  
#include
#include

void print(int i)
{
std::cout <}

int main()
{
std::vector v;
v.push_back(1);
v.push_back(3);
v.push_back(2);

std::for_each(v.begin(), v.end(), print);
}

    算法std::for_each要求它的第三个参数是一个仅接受正好一个参数的函数或函数对象。如果std::for_each被执行,指定容器中的所有元素将按顺序被传入print函数。但是,如果要使用一个具有不同签名的函数的话,事情就复杂了。如果要传入的是以下函数add,它要将一个常数值加至容器中的每个元素上,并显示结果。

void add(int i, int j) {   std::cout < 
 

     由于std::for_each()要求的是仅接受一个参数的函数,所以不能直接传入add()函数,必须要修改源码。

#include  #include  #include  #include  class add   : public std::binary_function { public:   void operator()(int i, int j) const   {     std::cout < v;   v.push_back(1);   v.push_back(3);   v.push_back(2);   std::for_each(v.begin(), v.end(), std::bind1st(add(), 10)); }

    以上程序将值10加至容器v的每个元素之上,并使用标准输出流显示结果。源代码必须作出大幅的修改,以实现此功能:add()函数已被转换为一个派生自std::binary_function的函数对象。

    Boost.Bind简化了不同函数之间的绑定。它只包含一个boost::bind()模板函数,定义于boost/bind.hpp中。使用这个函数,可以如下实现以上例子:

#include  #include  #include  #include  void add(int i, int j) {   std::cout < v;   v.push_back(1);   v.push_back(3);   v.push_back(2);   std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1)); } 

    象add这样的函数不再需要为了要用于std::for_each而转换为函数对象。使用 boost::bind,该函数可以忽略其第一个参数而使用。因为add函数要求两个参数,两个参数都必须传递给boost::bind。其中第一个参数是常数值10,而第二个参数则是一个比较奇怪的_1

    _1 被称为占位符(placeholder),定义于Boost.Bind。除了_1,Boost.Bind还定义了_2 和_3。通过使用这些占位符,boost::bind可以变为一元、二元或三元的函数。对于_1, boost::bind变成了一个一元函数。这是必需的,因为 std::for_each正是要求一个一元函数作为其第三个参数。

    当这个程序执行时,std::for_each对容器v中的第一个元素调用该一元函数。元素的值通过占位符_1 传入到一元函数中。这个占位符和常数值被进一步传递到add函数。通过使用这种机制,std::for_each只看到了由boost::bind所定义的一元函数。而boost::bind本身则只是调用了另一个函数,并将常数值或占位符作为参数传入给它。

    下面这个例子通过boost::bind定义了一个二元函数,用于std::sort算法,该算法要求一个二元函数作为其第三个参数。

#include  #include  #include  bool compare(int i, int j) {   return i > j; } int main() {   std::vector v;   v.push_back(1);   v.push_back(3);   v.push_back(2);   std::sort(v.begin(), v.end(), boost::bind(compare, _1, _2)); } 

    因为使用了两个占位符_1 和_2,所以boost::bind()定义了一个二元函数。 std::sort()算法以容器 v 的两个元素来调用该函数,并根据返回值来对容器进行排序。基于compare()函数的定义,容器将被按降序排列。

    但是,由于compare()本身就是一个二元函数,所以使用boost::bind()确是多余的。

#include  #include  #include  bool compare(int i, int j) {   return i > j; } int main() {   std::vector v;   v.push_back(1);   v.push_back(3);   v.push_back(2);   std::sort(v.begin(), v.end(), compare); } 

    不过使用boost::bind()还是有意义的。例如,如果容器要按升序排列而又不能修改 compare()函数的定义。

#include  #include  #include  bool compare(int i, int j) {   return i > j; } int main() {   std::vector v;   v.push_back(1);   v.push_back(3);   v.push_back(2);   std::sort(v.begin(), v.end(), boost::bind(compare, _2, _1)); } 

    该例子仅改变了占位符的顺序:_2 被作为第一参数传递,而 _1 则被作为第二参数传递至 compare(),这样即可改变排序的顺序。

二、Boost.Ref

    本库Boost.Ref通常与 Boost.Bind一起使用,所以我把它们挨着写。它提供了两个函数:boost::ref()和 boost::cref(),都定义于 boost/ref.hpp。

    当要用于boost::bind()的函数带有至少一个引用参数时,Boost.Ref就很重要了。 由于boost::bind()会复制它的参数,所以引用必须特别处理。

#include  #include  #include  #include  void add(int i, int j, std::ostream &os) {   os < v;   v.push_back(1);   v.push_back(3);   v.push_back(2);   std::for_each(v.begin(), v.end(), boost::bind(add, 10, _1, boost::ref(std::cout))); } 

    以上例子使用了上一节中的add()函数。 不过这一次该函数需要一个流对象的引用来打印信息。 因为传给boost::bind()的参数是以值方式传递的,所以std::cout不能直接使用,否则该函数会试图创建它的一份拷贝。

通过使用模板函数boost::ref(),象std::cout这样的流就可以被以引用方式传递,也就可以成功编译上面这个例子了。

    要以引用方式传递常量对象,可以使用模板函数boost::cref()。

三、Boost.Function

    为了封装函数指针,Boost.Function提供了一个名为boost::function的类。它定义于 boost/function.hpp,用法如下:

#include  #include  #include  #include  int main() {   boost::function f = std::atoi;   std::cout < 
 

    boost::function可以定义一个指针,指向具有特定签名的函数。以上例子定义了一个指针f,它可以指向某个接受一个类型为const char*的参数且返回一个类型为int的值的函数。定义完成后,匹配此签名的函数均可赋值给这个指针。这个例程就是先将std::atoi()赋值给f,然后再将它重赋值为std::strlen()。

    注意,给定的数据类型并不需要精确匹配,虽然std::strlen()是以std::size_t作为返回类型的,但是它也可以被赋值给f。

    因为f是一个函数指针,所以被赋值的函数可以通过重载的operator()()操作符来调用。取决于当前被赋值的是哪一个函数,在以上例子中将调用std::atoi()或std::strlen()。

    如果f未赋予一个函数而被调用,则会抛出一个boost::bad_function_call异常。

#include  #include  int main() {   try   {     boost::function f;     f("");   }   catch (boost::bad_function_call &ex)   {     std::cout < 
 

    注意,将值 0 赋给一个 boost::function 类型的函数指针,将会释放当前所赋的函数。释放之后再调用它也会导致 boost::bad_function_call 异常被抛出。要检查一个函数指针是否被赋值某个函数,可以使用 empty() 函数或 operator bool() 操作符。

   通过使用 Boost.Function,类成员函数也可以被赋值给类型为 boost::function 的对象。

#include  #include  struct world {   void hello(std::ostream &os)   {     os <<"Hello, world!" < f = &world::hello;   world w;   f(&w, boost::ref(std::cout)); } 

    在调用这样的一个函数时,传入的第一个参数表示了该函数被调用的那个特定对象。因此,在模板定义中的左括号后的第一个参数必须是该特定类的指针。 接下来的参数才是表示相应的成员函数的签名。

    这个程序还使用了来自Boost.Ref库的boost::ref(),它提供了一个方便的机制向 Boost.Function传递引用。








 





 


推荐阅读
  • 题目:poj2342Anniversaryparty题意:话说一个公司的一些然要去参加一个party,每个人有一个愉悦值,而如果某个人的直接上司在场的话会非常扫兴,所以避免这样 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 如何解决《使用std::ostream作为打印函数的参数》经验,为你挑选了1个好方法。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 本文主要介绍了gym102222KVertex Covers(高维前缀和,meet in the middle)相关的知识,包括题意、思路和解题代码。题目给定一张n点m边的图,点带点权,定义点覆盖的权值为点权之积,要求所有点覆盖的权值之和膜qn小于等于36。文章详细介绍了解题思路,通过将图分成两个点数接近的点集L和R,并分别枚举子集S和T,判断S和T能否覆盖所有内部的边。文章还提到了使用位运算加速判断覆盖和推导T'的方法。最后给出了解题的代码。 ... [详细]
  • 第3章 感受(一)——3.1. Hello world 经典版
    [回到目录]白话C++第3章.感受Helloworld!,HelloC++,我们来了!3.1.Helloworld经典版毫无疑义,一 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
author-avatar
Cher麻花
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有