博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

boost::bind, boost::function

Posted on 2014-12-15 22:19  bw_0927  阅读(262)  评论(0)    收藏  举报

http://www.cnblogs.com/my_life/articles/4152925.html

http://blog.csdn.net/solstice/article/details/3066268

以boost::function和boost:bind取代虚函数

muduo: page 448

 

boost::bind库绝对是最有用,最有价值的库之一,已被纳入tr1。bind库的出现,替代了stl中的mem_fun,ptr_fun,bind1st,bin2nd等函数


这是一篇比较情绪化的blog,中心思想是“继承就像一条贼船,上去就下不来了”,而借助boost::function和boost::bind,大多数情况下,你都不用上贼船。

boost::function和boost::bind已经纳入了std::tr1,这或许是C++0x最值得期待的功能,它将彻底改变C++库的设计方式,以及应用程序的编写方式。

Scott Meyers的Effective C++ 3rd ed.第35条款提到了以boost::function和boost:bind取代虚函数的做法,这里谈谈我自己使用的感受。


基本用途


boost::function就像C#里的delegate,可以指向任何函数,函数对象,包括成员函数。当用bind把某个成员函数绑到某个对象上时,我们得到了一个closure(闭包)(函数有了状态)。例如:
class Foo
{
 public:
  void methodA();     //包含一个隐藏的this指针作为第一个参数,相当于void Foo::methodA(Foo* this);
  void methodInt(int a);
};
class Bar
{
 public:
  void methodB();
};


Foo foo;
boost::function<void()> f1; // 无参数,无返回值;直接调用bind返回的函数对象,无需参数
f1 = boost::bind(&Foo::methodA, &foo);   //&foo作为Foo::methodA的第一个参数
f1(); // 调用 foo.methodA();


Bar bar;
f1 = boost::bind(&Bar::methodB, &bar);    //bind时没有使用任何占位符,所以返回的函数对象f1是0元函数
f1(); // 调用 bar.methodB();

f1 = boost::bind(&Foo::methodInt, &foo, 42);   //Foo::methodInt是类的成员函数,所以它至少需要一个参数来初始化this指针。因为methodInt()成员函数需要一个参数,所以用42来传入。
f1(); // 调用 foo.methodInt(42);

boost::function<void(int)> f2; // 函数对象f2: int 参数,无返回值;  调用bind返回的函数对象时需要提供一个参数
f2 = boost::bind(&Foo::methodInt, &foo, _1);   //bind返回的函数对象也是个一元函数,但具体的参数得在调用时才会知道,所以用了一个占位符_1,代表返回的函数对象是一个一元函数
f2(53); // 调用 foo.methodInt(53);     //实际调用时,才知道占位符_1的具体值是53

上面的3句可以简化为一句

boost::bind(&Foo::methodInt, &foo, _1)(53); 

或者更为简洁的

boost::bind(&Foo::methodInt, &foo, 53)(); 

 

 

bind()只是返回一个函数对象,被包装的函数不会被调用

 

非静态的成员函数必须通过对象来访问

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

==================================

其中,Boost.Bind 可替换来自C++标准的著名的 std::bind1st()std::bind2nd() 函数,Boost.Function 则提供了一个用于封装函数指针的类。 最后,Boost.Lambda 则引入了一种创建匿名函数的方法。

 

 

#include <boost/bind.hpp> 
#include <iostream> 
#include <vector> 
#include <algorithm> 

void add(int i, int j) 
{ 
  std::cout << i + j << std::endl; 
} 

int main() 
{ 
  std::vector<int> 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));   //10作为add的第一个参数,_1作为add的第二个参数(运行时才确定),值为for_each函数的第一个参数v.begin()代表的元素
} 
template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function fn)
{
  while (first!=last) {
    fn (*first);
    ++first;
  }
  return fn;      // or, since C++11: return move(fn);
}
 

for_each()的第三个参数是一个接受一个参数的函数对象

boost::bind(add, 10, _1)  返回接受一个参数的函数对象,被包装的add()函数接受两个参数,第一个是10,第二个是*first

 

算法 std::for_each() 要求它的第三个参数是一个仅接受正好一个参数的函数或函数对象。但add()是两个参数,用boost::bind完成封装

_1 被称为占位符(placeholder),定义于 Boost.Bind。 除了 _1,Boost.Bind 还定义了 _2_3通过使用这些占位符,boost::bind() 可以变为一元、二元或三元的函数。 对于 _1, boost::bind() 变成了一个一元函数 - 即只要求一个参数的函数。

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

 

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

#include <boost/bind.hpp> 
#include <vector> 
#include <algorithm> 

bool compare(int i, int j) 
{ 
  return i > j; 
} 

int main() 
{ 
  std::vector<int> 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的值是sort函数的第一个参数,_2的值是sort函数的第二个参数
} 

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

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

#include <boost/bind.hpp> 
#include <vector> 
#include <algorithm> 

bool compare(int i, int j) 
{ 
  return i > j; 
} 

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

  std::sort(v.begin(), v.end(), compare); 
} 

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

#include <boost/bind.hpp> 
#include <vector> 
#include <algorithm> 

bool compare(int i, int j) 
{ 
  return i > j; 
} 

int main() 
{ 
  std::vector<int> 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::bind() 的函数带有至少一个引用参数时,Boost.Ref 就很重要了。 由于 boost::bind() 会复制它的参数,所以引用必须特别处理。因为传给 boost::bind() 的参数是以值方式传递的,所以 std::cout 不能直接使用,否则该函数会试图创建它的一份拷贝。

通过使用模板函数 boost::ref(),象 std::cout 这样的流就可以被以引用方式传递。

===========

为了封装函数指针,Boost.Function 提供了一个名为 boost::function 的类。

匿名函数 - 又称为 lambda 函数

 

============

#include <iostream>
#include <boost/bind.hpp>
using namespace std;
class test_class
{
public:
    void fun(int i)
    {
        cout << i << endl;
    }
};
int _tmain(int argc, _TCHAR* argv[])
{
    test_class test;
    boost::bind(&test_class::fun,test,_1)(11);     //创建了一个yiyuan函数对象,函数的签名是test_class::fun,同时把test对象和_1参数绑定给该函数对象。   _1代表返回的函数对象是单目函数,其值是11
    return 0;
}

 

 

 

================================

http://www.cnblogs.com/yu-chao/p/3979124.html

3、bind绑定成员函数

类的成员函数不同于普通的函数,因为成员函数指针不能直接调用operator(),它必须被绑定到一个对象或指针,然后才能得到this指针进而调用成 员函数。因此bind需要 “牺牲”一个占位符,要求提供一个类的实例、引用或者指针,通过对象作为第一个参数来调用成员函数,即:

bind(&X::func,x,_1,_2,…)

这意味着使用成员函数时只能最多绑定8个参数。

 

注意:我们必须在成员函数前面加上取地址的操作符&,表明这是一个成员函数指针,否则会无法编译通过,这是与绑定函数的一个小小的不同。bind同样支持绑定虚拟成员函数,用法与非虚函数相同,虚函数的行为将由实际调用发生时的实例来决定。

 

 

==========================

http://www.boost.org/doc/libs/1_55_0/libs/bind/bind.html

 

  • 函数对象的bind形式: bind<R>(f, ...)

bind is not limited to functions; it accepts arbitrary function objects. In the general case, the return type of the generated function object's operator() has to be specified explicitly (without a typeof operator the return type cannot be inferred):

struct F
{
    int operator()(int a, int b) { return a - b; }
    bool operator()(long a, long b) { return a == b; }
};

F f;

int x = 104;

bind<int>(f, _1, _1)(x);		// f(x, x), i.e. zero     

Some compilers have trouble with the bind<R>(f, ...) syntax. For portability reasons, an alternative way to express the above is supported:

boost::bind(boost::type<int>(), f, _1, _1)(x);


Inappropriate use of bind<R>(f, ...)

The bind<R>(f, a1, a2, ..., aN) form supports arbitrary function objects.    //这种形式适用于函数对象、仿函数,可以但不推荐用在裸函数或者成员函数上

It is possible (but not recommended) to use this form with functions or member function pointers, but only on compilers that support partial ordering.

 

  • Using bind with pointers to members   成员函数的bind形式

Pointers to member functions and pointers to data members are not function objects, because they do not support operator().

For convenience, bind accepts member pointers as its first argument, and the behavior is as if boost::mem_fn(把成员函数转换成函数对象) has been used to convert the member pointer into a function object. In other words, the expression

bind(&X::f, args)

is equivalent to

bind<R>(mem_fn(&X::f), args)

where R is the return type of X::f (for member functions) or the type of the member (for data members.)

 

http://www.boost.org/doc/libs/1_60_0/libs/bind/doc/html/bind.html#bind.faq.Q_forms

The first form instructs bind to inspect the type of f in order to determine its arity (number of arguments) and return type. Arity errors will be detected at "bind time". This syntax, of course, places some requirements on f. It must be a function, function pointer, member function pointer, or a function object that defines a nested type named result_type; in short, it must be something that bind can recognize.

第一种方式需要bind对f进行探测以决定返回值和参数个数,即需要匹配函数原型

其中f可以是函数,函数指针,成员函数指针,或者定义了嵌套类型result_type的函数对象

The second form instructs bind to not attempt to recognize the type of f. It is generally used with function objects that do not, or cannot, expose result_type, but it can also be used with nonstandard functions. For example, the current implementation does not automatically recognize variable-argument functions like printf, so you will have to use bind<int>(printf...). Note that an alternative bind(type<R>(), f...) syntax is supported for portability reasons.

第二种方式bind不尝试进行类型检测,通常它用于那些不能够或者没有nested result_type的函数对象

Another important factor to consider is that compilers without partial template specialization or function template partial ordering support cannot handle the first form when f is a function object, and in most cases will not handle the second form when f is a function (pointer) or a member function pointer.

另外需要注意的一点是:那些不支持模版偏特化或者function template partial ordering support 的编译器,不能处理第一种形式,当f是一个函数对象;不多数情况下,也不能处理第二种情况,当f是函数(指针)或者成员函数指针