boost::bind 详解
原文:https://www.cnblogs.com/benxintuzi/p/4862129.html
使用
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 = 100;
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 = 5;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,int x) const{//要添加个int参数return f(x);}
};
示例程序
// 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 + 10 * 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 + 10 * b; }void function_test()
{using namespace boost;int const i = 1;BOOST_TEST( bind(f_0)(i) == 17041L );BOOST_TEST( bind(f_1, _1)(i) == 1L );BOOST_TEST( bind(f_2, _1, 2)(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, 2)(i), (global_result == 21L)) );
}//
struct Y
{short operator()(short & r) const { return ++r; }int operator()(int a, int b) const { return a + 10 * b; }
};void function_object_test()
{using namespace boost;short i(6);BOOST_TEST( bind<short>(Y(), ref(i))() == 7 );BOOST_TEST( bind(type<short>(), Y(), ref(i))() == 8 );
}//
struct X
{mutable unsigned int hash;X(): hash(0) {}int f0() { f1(17); return 0; }int g0() const { g1(17); return 0; }int f1(int a1) { hash = (hash * 17041 + a1) % 32768; return 0; }int g1(int a1) const { hash = (hash * 17041 + a1 * 2) % 32768; return 0; }int f2(int a1, int a2) { f1(a1); f1(a2); return 0; }int g2(int a1, int a2) const { g1(a1); g1(a2); return 0; }
};void member_function_test()
{using namespace boost;X x;// 0bind(&X::f0, &x)();bind(&X::f0, ref(x))();bind(&X::g0, &x)();bind(&X::g0, x)();bind(&X::g0, ref(x))();// 1bind(&X::f1, &x, 1)();bind(&X::f1, ref(x), 1)();bind(&X::g1, &x, 1)();bind(&X::g1, x, 1)();bind(&X::g1, ref(x), 1)();// 2bind(&X::f2, &x, 1, 2)();bind(&X::f2, ref(x), 1, 2)();bind(&X::g2, &x, 1, 2)();bind(&X::g2, x, 1, 2)();bind(&X::g2, ref(x), 1, 2)();
}void nested_bind_test()
{using namespace boost;int const x = 1;int const y = 2;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, 1); // error, f takes two argumentsboost::bind(f, 1, 2); // OK
}
一个此类错误的变形为:忘记成员函数有一个隐式参数this:
struct X
{int f(int);
}int main()
{boost::bind(&X::f, 1); // error, X::f takes two argumentsboost::bind(&X::f, _1, 1); // OK
}
2. 函数对象不能被指定参数调用
int f(int);int main()
{boost::bind(f, "incompatible"); // OK so far, no callboost::bind(f, "incompatible")(); // error, "incompatible" is not an intboost::bind(f, _1); // OKboost::bind(f, _1)("incompatible"); // error, "incompatible" is not an int
}
3. 访问不存在的参数
占位符_N需要在调用时从指定的参数表中选择第N个参数:
int f(int);int main()
{boost::bind(f, _1); // OKboost::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, 1); // error
}
// 7.2 MSVC 7.0以下编译器
(1) 如果通过using声明引入boost::bind,如:using boost::bind,那么bind<R>(f, ...)语法将不能工作。
解决办法为直接使用限定名boost::bind或者使用using指令:using namespace boost;
(2) 一个嵌套的命名为bind的类模板将隐藏函数模板boost::bind,使得bind<R>(f, ...)语法不能工作。
(3) 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 详解相关推荐
- 【c++】24.std::function和std::bind详解
1. 可调用对象 查看全文 http://www.taodudu.cc/news/show-494578.html 相关文章: [c++]26.浅谈"事件驱动".select.po ...
- std::bind 详解及参数解析
// Bind_std_function.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> ...
- JavaScript call,apply,bind详解及实现
1.前言 call,apply,bind这三个方法都是用来改变函数的this指向,如果有对this不熟悉的朋友,可以先看看笔者的这篇博客. call & apply call()语法: fun ...
- Linux系统DNS详解(BIND)
一.Linux运维实战之DNS基础 DNS服务作为网络的一种基础架构,在网络中有举足轻重的地位.它担负着整个网络用户计算机的名称解析工作.没有正确的名称解析,服务器就无法识别各客户机.我们在日 ...
- Linux下boost库的编译、安装详解
1.下载源文件 去官网下载:http://www.boost.org/ 这里下载最新版本 wget https://dl.bintray.com/boostorg/release/1.64.0/sou ...
- [转] boost::function用法详解
http://blog.csdn.net/benny5609/article/details/2324474 要开始使用 Boost.Function, 就要包含头文件 "boost/fun ...
- call(),apply()和bind()的详解使用:
obj.call(thisObj, arg1, arg2, ...); obj.apply(thisObj, [arg1, arg2, ...]); 两者作用一致,都是把obj(即this)绑定到th ...
- bind() c语言,c/c++ 标准库 bind 函数详解
bind函数定义在头文件 functional 中.可以将 bind 函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来"适应"原对象的参数列表. bin ...
- BIND配置文件详解(二)
本文档摘录自<BIND9管理员手册>,如果有不对或者不清楚的地方,请大家告诉我,谢谢! BIND配置文件详解(二) 6.options语句 options语句的定义和使用: opt ...
- boost::function用法详解
要开始使用 Boost.Function, 就要包含头文件 "boost/function.hpp", 或者某个带数字的版本,从 "boost/function/func ...
最新文章
- rhel-server-7.5-x86_64-dvd.iso镜像下载及rar压缩包的解压
- HarmonyOS之深入解析媒体会话的管理
- MySQL高级 - 日志 - 慢查询日志
- 高并发场景下的缓存有哪些常见的问题?
- SpringBoot启动报错java.nio.charset.MalformedInputException: Input length = 2解决方案
- 【Cisco技术资料汇总】
- Ubuntu下安装中文输入法
- mysql要将语句反复执行15次_MySQL多表查询疑问
- 动态链接库的隐式动态链接和显示动态链接
- python runner功能_Python 如何使用 HttpRunner 做接口自动化测试
- Webb.WAVE项目开发体会与心得
- java毕业设计博弈论学习网站Mybatis+系统+数据库+调试部署
- 什么是决策!决策的定义!决策的本质!大数据决策定义!
- linux磁盘写保护怎么修改_linux
- 配对t检验的应用条件是什么_配对t检验在实际工作中的应用
- java截取文件名后缀
- pvr文件转成png和plist
- centos7部署rap2
- 公司测试用例评审的简单介绍
- 工具推荐:用VS code 导出、导入和运行Excel中的VBA代码
热门文章
- 标量与向量乘积求导法则
- Mac Finder不显示侧边栏
- AutoSar之CAN网络管理详解
- ivx中字体显示_【初阶篇】iVX成语填字游戏制作
- 机器人感知与规划笔记 (7) - 行为架构 (Behavioral Architectures)
- ArcGIS 解决影像裁剪后锯齿问题
- highcharts的柱状图显示数据
- δ星 丨 读书笔记 notes-凭什么《只放一只羊》:干掉沃尔玛10个亿并将其逼出德国的“平民超市”品牌阿尔迪...
- 华为公司是如何做绩效考核的
- java 继承是什么_java中继承指的是什么