使用

boost::bind是标准库函数std::bind1st和std::bind2nd的一种泛化形式。其可以支持函数对象、函数、函数指针、成员函数指针,并且绑定任意参数到某个指定值上或者将输入参数传入任意位置。

1. 通过functions和function pointers使用bind

给定如下函数:

 int f(int a, int b)
{
return a + b;
} int g(int a, int b, int c)
{
return a + b + c;
}

可以绑定所有参数,如:

bind(f, 1, 2)等价于f(1, 2); bind(g, 1, 2, 3)等价于g(1, 2, 3);

也可以选择性地绑定参数,如:

bind(f, _1, 5)(x)等价于f(x, 5),其中_1是一个占位符,表示用第一个参数来替换;

bind(f, _2, _1)(x, y)等价于f(y, x);

bind(g, _1, 9, _1)(x)等价于g(x, 9, x);

bind(g, _3, _3, _3)(x, y, z)等价于g(z, z, z);

说明:

传入bind函数的参数一般为变量的copy,如:

int i = 5;

bind(f, i, _1);

如果想传入变量的引用,可以使用boost::ref和boost::cref,如:

int i = 5;

bind(f, ref(i), _1);

bind(f, cref(i), _1);

2. 通过function objects使用bind

 struct F
{
int operator()(int a, int b) { return a – b; }
bool operator()(long a, long b) { return a == b; }
}; F f;
int x = ;
bind<int>(f, _1, _1)(x); // f(x, x)

可能某些编译器不支持上述的bind语法,可以用下列方式代替:

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

默认情况下,bind拥有的是函数对象的副本,但是也可以使用boost::ref和boost::cref来传入函数对象的引用,尤其是当该function object是non-copyable或者expensive to copy。

3. 通过pointers to members使用bind

bind将传入的成员(数据成员和成员函数)指针作为第一个参数,其行为如同使用boost::mem_fn将成员指针转换为一个函数对象,即:

bind(&X::f, args);       等价于bind<R>(mem_fn(&X::f), args),其中R为X::f的返回类型(成员函数)或类型(数据成员)。

 struct X
{
bool f(int a);
}; X x;
shared_ptr<X> p(new X);
int i = ; bind(&X::f, ref(x), _1)(i); // x.f(i)
bind(&X::f, &x, _1)(i); // (&x)->f(i)
bind(&X::f, x, _1)(i); // x.f(i)
bind(&X::f, p, _1)(i); // p->f(i)

4. 使用nested binds

如bind(f, bind(g, _1))(x)中:

在外部bind计算之前,内部bind先被计算(如果内部有多个bind,则计算顺序不定)。如上,根据参数x,先计算bind(g, _1)(x),生成g(x),然后计算bind(f, g(x))(x),最后生成f(g(x))。

 

但是要注意:

bind中的第一个参数不参与计算过程,假设如下程序想要实现对于向量v中的每个函数指针,传入一个参数 5:

typedef void (*pf)(int);

std::vector<pf> v;

std::for_each(v.begin(), v.end(), bind(_1, 5));

上述程序并没有实现我们想要的结果:可以通过使用一个帮助函数对象apply,该对象可以将bind的第一个参数作为一个函数对象,如下:

typedef void (*pf)(int);

std::vector<pf> v;

std::for_each(v.begin(), v.end(), bind(apply<void>(), _1, 5));

其中,apply实现如下:

 template<class R>
struct apply
{
typedef R result_type;
template<class F> result_type operator()(F & f) const
{
return f();
}
...
};

示例程序

 // bind_test.cc
#include <boost/config.hpp> #if defined(BOOST_MSVC)
#pragma warning(disable: 4786) // identifier truncated in debug info
#pragma warning(disable: 4710) // function not inlined
#pragma warning(disable: 4711) // function selected for automatic inline expansion
#pragma warning(disable: 4514) // unreferenced inline removed
#endif #include <boost/bind.hpp>
#include <boost/ref.hpp> #if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)
#pragma warning(push, 3)
#endif #include <iostream> #if defined(BOOST_MSVC) && (BOOST_MSVC < 1300)
#pragma warning(pop)
#endif #include <boost/detail/lightweight_test.hpp> //
long f_0() { return 17041L; }
long f_1(long a) { return a; }
long f_2(long a, long b) { return a + * b; } long global_result; void fv_0() { global_result = 17041L; }
void fv_1(long a) { global_result = a; }
void fv_2(long a, long b) { global_result = a + * b; } void function_test()
{
using namespace boost; int const i = ; BOOST_TEST( bind(f_0)(i) == 17041L );
BOOST_TEST( bind(f_1, _1)(i) == 1L );
BOOST_TEST( bind(f_2, _1, )(i) == 21L ); BOOST_TEST( (bind(fv_0)(i), (global_result == 17041L)) );
BOOST_TEST( (bind(fv_1, _1)(i), (global_result == 1L)) );
BOOST_TEST( (bind(fv_2, _1, )(i), (global_result == 21L)) );
} //
struct Y
{
short operator()(short & r) const { return ++r; }
int operator()(int a, int b) const { return a + * b; }
}; void function_object_test()
{
using namespace boost;
short i(); BOOST_TEST( bind<short>(Y(), ref(i))() == );
BOOST_TEST( bind(type<short>(), Y(), ref(i))() == );
} //
struct X
{
mutable unsigned int hash; X(): hash() {} int f0() { f1(); return ; }
int g0() const { g1(); return ; } int f1(int a1) { hash = (hash * + a1) % ; return ; }
int g1(int a1) const { hash = (hash * + a1 * ) % ; return ; } int f2(int a1, int a2) { f1(a1); f1(a2); return ; }
int g2(int a1, int a2) const { g1(a1); g1(a2); return ; }
}; void member_function_test()
{
using namespace boost; X x; //
bind(&X::f0, &x)();
bind(&X::f0, ref(x))();
bind(&X::g0, &x)();
bind(&X::g0, x)();
bind(&X::g0, ref(x))(); //
bind(&X::f1, &x, )();
bind(&X::f1, ref(x), )();
bind(&X::g1, &x, )();
bind(&X::g1, x, )();
bind(&X::g1, ref(x), )(); // bind(&X::f2, &x, , )();
bind(&X::f2, ref(x), , )();
bind(&X::g2, &x, , )();
bind(&X::g2, x, , )();
bind(&X::g2, ref(x), , )();
} void nested_bind_test()
{
using namespace boost; int const x = ;
int const y = ; BOOST_TEST( bind(f_1, bind(f_1, _1))(x) == 1L );
BOOST_TEST( bind(f_1, bind(f_2, _1, _2))(x, y) == 21L );
} int main()
{
function_test();
function_object_test();
member_function_test();
nested_bind_test(); return boost::report_errors();
} //output
No errors detected.

易错点

1. 参数个数不正确

 int f(int, int);

 int main()
{
boost::bind(f, ); // error, f takes two arguments
boost::bind(f, , ); // OK
}
一个此类错误的变形为:忘记成员函数有一个隐式参数this:
struct X
{
int f(int);
} int main()
{
boost::bind(&X::f, ); // error, X::f takes two arguments
boost::bind(&X::f, _1, ); // OK
}

2. 函数对象不能被指定参数调用

 int f(int);

 int main()
{
boost::bind(f, "incompatible"); // OK so far, no call
boost::bind(f, "incompatible")(); // error, "incompatible" is not an int
boost::bind(f, _1); // OK
boost::bind(f, _1)("incompatible"); // error, "incompatible" is not an int
}

3. 访问不存在的参数

 占位符_N需要在调用时从指定的参数表中选择第N个参数:
int f(int); int main()
{
boost::bind(f, _1); // OK
boost::bind(f, _1)(); // error, there is no argument number 1
}

4. bind(f, ...)形式和bind<R>(f, ...)形式的不当用法

 bind(f, a1, a2, ..., aN)会对f自动进行类型识别,f必须是一个函数或者成员函数指针。当f是函数对象时,大多数编译器将不能工作。
bind<R>(f, a1, a2, ..., aN)支持任意类型的函数对象。虽然在有些编译器上,传入函数或者成员函数指针也能工作,但是不推荐这么做。

5. 绑定一个非标准函数

 bind(f, a1, a2, ..., aN)形式识别<普通的>C++函数和成员函数指针。如果一个函数使用了不同的调用约定或者可变参数列表(如std::printf),那么bind(f, a1, a2, ..., aN)将不能工作;如果确实需要使用此类非标准函数,那么bind<R>(f, a1, a2, ..., aN)将能满足这种要求。

6. 绑定一个重载函数

 通常情况下,bind一个重载函数会导致错误,因为无法确定到底bind重载函数的哪个形式:
struct X
{
int& get();
int const& get() const;
}; int main()
{
boost::bind(&X::get, _1);
}
这种二义性可以通过将成员函数指针转换到特定类型来解决:
int main()
{
boost::bind(static_cast< int const& (X::*) () const >(&X::get), _1);
}
此外,一个更可具可读性的解决办法为引入一个临时变量: int main()
{
int const& (X::*get) () const = &X::get;
boost::bind(get, _1);
}

7. boost的特定编译器实现问题

 // 7.1 MSVC 6.0编译器
在函数签名中不支持const:(移除const就可以了)
int f(int const); int main()
{
boost::bind(f, ); // error
}
// 7.2 MSVC 7.0以下编译器
() 如果通过using声明引入boost::bind,如:using boost::bind,那么bind<R>(f, ...)语法将不能工作。
解决办法为直接使用限定名boost::bind或者使用using指令:using namespace boost;
() 一个嵌套的命名为bind的类模板将隐藏函数模板boost::bind,使得bind<R>(f, ...)语法不能工作。
() MSVC将可变参数中的省略号看作一种类型,因此,其可以接受如下形式:
bind(printf, "%s\n", _1);
但是拒绝正确的形式如:
bind<int>(printf, "%s\n", _1);

调用约定

根据调用约定的不同,不同的平台可能支持几种类型的(成员)函数。例如:

Windows API函数和COM接口成员函数使用__stdcall;

Borland VCL使用__fastcall;

Mac toolbox函数使用pascal。

与__stdcall函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_STDCALL;

与__stdcall成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_STDCALL;

与__fastcall函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_ FASTCALL;

与__fastcall成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_ FASTCALL;

与pascal函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_BIND_ENABLE_ PASCAL;

与__cdecl成员函数一起使用bind时,在包含<boost/bind.hpp>之前#define the macro BOOST_MEM_FN_ENABLE_CDECL;

一个比较好的建议是:如果需要使用bind,要么提前在工程选项中定义这些宏,要么通过命令行选项-D定义,要么直接在使用bind的.cc文件中定义。否则如果包含bind.hpp的文件中,发生了在定义这些宏之前including bind.hpp,那么可能导致难以发现的错误。

boost::bind 详解的更多相关文章

  1. Boost 安装详解

    一 Linux(redhat)篇 1.1 获取boost库 解压tar -zxvf boost_1.48.0.tar.gz 进入解压目录cd boost_1_48_0 1.2 编译安装 使用下面的命令 ...

  2. 原生JS:Function对象(apply、call、bind)详解

    Function对象(apply.call.bind) 原创文章,转摘请注明出处:苏福:http://www.cnblogs.com/susufufu/p/5850180.html 本文参考MDN做的 ...

  3. socket bind详解

    http://www.cnblogs.com/nightwatcher/archive/2011/07/03/2096717.html 在最开始接触bind的时候,只是在写基于tcp的server端的 ...

  4. javascript中的apply,call,bind详解

    apply.call 在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向. Jav ...

  5. call Apply bind详解

    call方法: 语法:call(thisObj,'',''........) 定义:调用一个对象的一个方法,以另一个对象替换当前对象 说明:call方法可以用来代替另一个对象调用一个方法.call方法 ...

  6. 【Boost】boost::tokenizer详解

    分类: [C++]--[Boost]2012-12-28 21:42 2343人阅读 评论(0) 收藏 举报   目录(?)[+]   tokenizer 库提供预定义好的四个分词对象, 其中char ...

  7. boost::tokenizer详解

    tokenizer 库提供预定义好的四个分词对象, 其中char_delimiters_separator已弃用. 其他如下: 1. char_separator char_separator有两个构 ...

  8. 【Boost】boost::string_algo详解2——find相关函数

    来自: https://blog.csdn.net/huang_xw/article/details/8276123 函数声明:   template<typename Range1T, typ ...

  9. std::bind 详解及参数解析

    // Bind_std_function.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> ...

随机推荐

  1. centos 7 生成文件名乱码的问题如何解决?

    在应用脚本生成文件时,发现生成的文件名称出现乱码(似麻将一样).刚开始找来找去,以为是复制粘贴的原因,复制时复制了特殊字符导致的,结果修改源文件后发现生成的文件名还是乱码.后来在执行脚本时,提示&qu ...

  2. C语言指针的易错点

    1.内存泄漏:申请的堆内存没有释放. 2.内存污染:前面非法操作使用内存(没有报错),后面写着写着就出错.如下代码: 当结构体中只有划线部分代码时,在编译器中编写不会报错,但此时已经造成非法操作内存, ...

  3. SS不能在Win7中打开,出现停止运行

    一次,在Win7上不能打开SS,经过搜索,好像SS的win客户端使用.net frame4.6.2开发,但是Win7根本安装不了该版本的.net,所以...,重新安装Win10.

  4. atitit.词法分析原理 词法分析器 (Lexer)

    atitit.词法分析原理 词法分析器 (Lexer) 1. 词法分析(英语:lexical analysis)1 2. :实现词法分析程序的常用途径:自动生成,手工生成.[1] 2 2.1. 词法分 ...

  5. [css]后台管理系统布局

    知识点: 绝对定位+overflowhidden 整体思路 三大块 pg-header---需要固定 (height:48px) pg-content menu 右侧菜单-需要固定(width:200 ...

  6. [gj]耶稣和撒旦的关系

    转: https://zhidao.baidu.com/question/7461904.html 人生充满试探,无论你居住在乡间或城市,都尝会受到试探,耶稣在世上的日子,也受到试探,让我们看看两处经 ...

  7. C++语言基础(24)-四种类型转换运算符(static_cast、dynamic_cast、const_cast和reinterpret_cast)

    一.static_cast static_cast 只能用于良性转换,这样的转换风险较低,一般不会发生什么意外,如: #include <iostream> #include <cs ...

  8. mongodb 安装部署说明

    mongodb.conf 配置文件 # Where the databases will be stored dbpath=/usr/local/mongodb/mongodb-/data/db # ...

  9. c++11 thread (目前我使用的ZThread库)

    目前为止(2014-11-30),GCC其实已经基本上完全支持C++11的所有功能了,事实上从GCC4.7之后,就支持了-std=c++11选项,在4.7版本之前,也开始支持-std=c++0x的选项 ...

  10. 跑测试没有web环境的情况

    有时候 当你跑测试的main方法的时候,会有一些莫名其妙的错误,明明mave pom的包是全的,web跑起来不会报错,可是在main方法下就是报错了,这个时候引入  <dependency> ...