文章目录

  • C++ bind浅析
    • 1. bind1st
      • 1.1 基本使用
      • 1.2 `std::bind1st`原理
    • 2 bind
      • 2.1 使用
      • 2.2 std::bind 原理
      • 2.3 operator() 实现
    • 3. 总结

C++ bind浅析

使用过STL的人肯定知道,STL有一个适配器的概念,其中stack, queue都是适配器,底层使用其他的数据结构来实现。

除了容器有适配器之外,其实函数也提供了适配器,适配器的特点就是将一个类型改装成为拥有子集功能的新的类型。其中函数的适配器典型的就是通过std::bind来实现。

本文我们看下std::bind的原理。

1. bind1st

在了解std::bind之前,我们先看一个简单的情况std::bind1st,这个函数相当我们可以绑定第一个值,可以将一个二元操作转换为一元操作。

1.1 基本使用

void bind_test()
{auto is_large_ten = std::bind1st(std::less<int>(), 10);std::cout << is_large_ten(5) << std::endl;std::cout << is_large_ten(15) << std::endl;
}
int main()
{bind_test();return 0;
}

如果我们需要判断一个数是否小于10,那么因为10是固定的,相当只要传入一个变量即可,因此这里使用函数适配器将std::less适配成为is_large_ten.

1.2 std::bind1st原理

其实这个函数是非常简单的,返回一个对象给调用者,这个对象有如下特性:

  1. 保存被适配的函数。
  2. 保存被适配函数的第一个参数的值。
  3. 实现调用操作result_type operator()(const argument_type& _Right).
  4. 在调用操作中调用被适配函数,第一个参数为绑定的值,第二个参数为传递的参数。
template<class _Fn2>class binder1st: public unary_function<typename _Fn2::second_argument_type,typename _Fn2::result_type>{  // functor adapter _Func(stored, right)
public:typedef unary_function<typename _Fn2::second_argument_type,typename _Fn2::result_type> _Base;typedef typename _Base::argument_type argument_type;typedef typename _Base::result_type result_type;binder1st(const _Fn2& _Func,const typename _Fn2::first_argument_type& _Left): op(_Func), value(_Left){    // construct from functor and left operand}result_type operator()(const argument_type& _Right) const{   // apply functor to operandsreturn (op(value, _Right));}result_type operator()(argument_type& _Right) const{    // apply functor to operandsreturn (op(value, _Right));}protected:_Fn2 op;  // the functor to applytypename _Fn2::first_argument_type value;    // the left operand};// TEMPLATE FUNCTION bind1st
template<class _Fn2,class _Ty> inlinebinder1st<_Fn2> bind1st(const _Fn2& _Func, const _Ty& _Left){  // return a binder1st functor adaptertypename _Fn2::first_argument_type _Val(_Left);return (binder1st<_Fn2>(_Func, _Val));}

从上面代码我们可以很简单的发现:

  1. 返回对象的类型为binder1st.
  2. 使用typename _Fn2::first_argument_type value;保存了绑定的值。
  3. _Fn2 op; 定义了被适配的函数。
  4. 提供调用操作符result_type operator()(argument_type& _Right).
  5. 调用操作中,使用被适配的函数,第一个值为绑定的值return (op(value, _Right));.

与之类似,还存在一个绑定第二个参数的适配器为std::bind2nd,这个函数适配器的原理和std::bind1st类似,这里不再分析。

2 bind

标准库中,认为一元操作和二元操作是非常常用的算法操作,但是三元以及以上是极少的,因此标准库中并没有定义三元函数和其适配器。

但是提供了更加通用的东西std::bind,来绑定参数信息。

2.1 使用

bind是一个函数的适配器,可以将一个函数的参数特例化,如下:

int fun(int a, int b, int c, int d)
{std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl;std::cout << d << std::endl;return 0;
}
void bind_test()
{auto f = std::bind(fun, std::placeholders::_1, std::placeholders::_2, 300, 400);f(100, 200);
}
int main()
{bind_test();return 0;
}

其中std::placeholders::_1是一个占位符号。接下来我们看下std::bind的具体实现原理。

2.2 std::bind 原理

std::bind是一个模板函数,这个函数的主要目的是创建一个_Binder函数适配器对象,代码如下:

template<class _Fx,class... _Types>_NODISCARD inline _Binder<_Unforced, _Fx, _Types...> bind(_Fx&& _Func, _Types&&... _Args){    // bind a callable object with an implicit return typereturn (_Binder<_Unforced, _Fx, _Types...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...));}

适配器对象的实现如下:

template<class _Ret,class _Fx,class... _Types>class _Binder: public _Binder_result_type<_Ret, _Fx>::type{    // wrap bound callable object and arguments
private:typedef index_sequence_for<_Types...> _Seq;typedef decay_t<_Fx> _First;typedef tuple<decay_t<_Types>...> _Second;_Compressed_pair<_First, _Second> _Mypair;public:explicit _Binder(_Fx&& _Func, _Types&&... _Args): _Mypair(_One_then_variadic_args_t(),_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...){    // construct from forwarded callable object and arguments}#define _BINDER_OPERATOR(CONST_OPT) \template<class... _Unbound> \auto operator()(_Unbound&&... _Unbargs) CONST_OPT \-> decltype(_Call_binder(_Invoker_ret<_Ret>(), _Seq(), \_Mypair._Get_first(), _Mypair._Get_second(), \_STD forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...))) \{   /* invoke bound callable object with bound/unbound arguments */ \return (_Call_binder(_Invoker_ret<_Ret>(), _Seq(), \_Mypair._Get_first(), _Mypair._Get_second(), \_STD forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...))); \}_CLASS_DEFINE_CONST(_BINDER_OPERATOR)
#undef _BINDER_OPERATOR};

这个实现过程如下:

  1. decay_t<_Fx> _First : 这个声明一个Binder函数的类型。
  2. tuple<decay_t<_Types>...> _Second : 绑定的参数的元组。
  3. _Compressed_pair<_First, _Second> _Mypair将函数和参数元组组成一个pair。
  4. 实现函数调用操作auto operator().

所以结构信息如下:

2.3 operator() 实现

_Binder的实现过程如下:

template<class... _Unbound> \
auto operator()(_Unbound&&... _Unbargs) CONST_OPT \
-> decltype(_Call_binder(_Invoker_ret<_Ret>(), _Seq(), \_Mypair._Get_first(), _Mypair._Get_second(), \_STD forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...))) \
{   /* invoke bound callable object with bound/unbound arguments */ \
return (_Call_binder(_Invoker_ret<_Ret>(), _Seq(), \_Mypair._Get_first(), _Mypair._Get_second(), \_STD forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...))); \
}

这个函数的返回值有点复杂,但是不是我们关系的,这里主要看一下_Call_binder的执行过程:

  1. _Mypair._Get_first() : 获取到函数对象。
  2. _Mypair._Get_second() : 获取绑定的参数列表。
  3. forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...)) : 参数元组。

_Call_binder的实现如下:

template<class _Ret,size_t... _Ix,class _Cv_FD,class _Cv_tuple_TiD,class _Untuple> inlineauto _Call_binder(_Invoker_ret<_Ret>, index_sequence<_Ix...>,_Cv_FD& _Obj, _Cv_tuple_TiD& _Tpl, _Untuple&& _Ut)-> decltype(_Invoker_ret<_Ret>::_Call(_Obj, _Fix_arg(_STD get<_Ix>(_Tpl), _STD move(_Ut))...)){ // bind() and bind<R>() invocationreturn (_Invoker_ret<_Ret>::_Call(_Obj, _Fix_arg(_STD get<_Ix>(_Tpl), _STD move(_Ut))...));}

参数信息如下:

调用函数的时候,会将_Ut元组中的参数替换Tpl中的占位符,因此也就是说,占位符位置可以随意,如下:

int fun(int a, int b, int c, int d)
{std::cout << a << std::endl;std::cout << b << std::endl;std::cout << c << std::endl;std::cout << d << std::endl;return 0;
}
void bind_test()
{auto f = std::bind(fun, 100, std::placeholders::_2, 300, std::placeholders::_1);f(100, 200);
}
int main()
{bind_test();return 0;
}

3. 总结

从上来看,std::bind其实就是一个函数的适配器,用来绑定其中的参数的特定值,实现了一个特化版本的函数。

C++ bind浅析相关推荐

  1. DNS浅析-Bind软件的使用及搭建一个简单的DNS缓存服务器

    引入DNS软件: Bind:Berkey Information Name Domain 对数据库的支持不太好,不支持多线程 PowerDNS 性能强大 速  度块 Bind软件的配置使用 yum l ...

  2. 方法apply作用于对象sort时失败_浅析call、apply 与 bind

    点击上方蓝色字体轻松关注 前言 经典模式题:call.apply 与 bind的区别.来吧,今天搞一搞. call(thisArgs [,args...]) 该方法可以传递一个thisArgs参数和一 ...

  3. Netty框架Bind流程浅析

    Netty简介: Netty是JBOSS 基于Java NIO开源的提供异步.事件驱动的网络通信框架,用于开发高性能网络服务器 Zookeeper RocketMq Dubbo ShardingSph ...

  4. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  5. Python标准库asyncio模块基本原理浅析

    Python标准库asyncio模块基本原理浅析 本文环境python3.7.0 asyncio模块的实现思路 当前编程语言都开始在语言层面上,开始简化对异步程序的编程过程,其中Python中也开始了 ...

  6. 服务端异步IO配合协程浅析

    服务端异步IO配合协程浅析 代码如下 #coding:utf-8 import socket from selectors import DefaultSelector, EVENT_READ, EV ...

  7. amqp协议与pika库浅析

    AMQP协议 简介 高级消息队列协议使得遵从该规范的客户端应用和消息中间件服务器的全功能互操作成为可能. 为了完全实现消息中间件的互操作性,需要充分定义网络协议和消息代理服务的功能语义. 一套确定的消 ...

  8. 转载:JMS-ActiveMQ浅析

    ActiveMQ 即时通讯服务 浅析 一. 概述与介绍 ActiveMQ 是Apache出品,最流行的.功能强大的即时通讯和集成模式的开源服务器.ActiveMQ 是一个完全支持JMS1.1和J2EE ...

  9. 浅析React之事件系统(二)

    上篇文章中,我们谈到了React事件系统的实现方式,和在React中使用原生事件的方法,那么这篇文章我们来继续分析下,看看React中合成事件和原生事件混用的各种情况. 上一个例子 在上篇文章中,我们 ...

最新文章

  1. 使用基本几何图元在道路上实现准确高效的自定位
  2. Python代码发送post请求接口测试--转载
  3. 关于启明星辰IDS无法CONSOLE连接
  4. django 用户管理(1)
  5. 【LeetCode笔记】206. 反转链表(Java、迭代、递归、链表)
  6. 搭建Harbor私有仓库
  7. RabbitMQ 基本概念介绍-----转载
  8. 性能测试:基础(1)
  9. java applet运行jmx,通过tomcat设置jvm及添加jmx远程访问、gc输出日志
  10. C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 大型软件系统客户端数据同步的问题解决...
  11. python数据分析与挖掘实战---chapter8中医证型关联规则挖掘
  12. php tp admin怎么安装,TP的配置及安装 · Thinkphp5.1+AdminLTE内容管理系统(商城版) 请不要购买 到群上下载源码 · 看云...
  13. Windows漏洞补丁更新网址
  14. RuntimeError: Cannot re-initialize CUDA in forked subprocess. 一个奇怪bug的奇妙解决方法
  15. javascript错误_JavaScript开发人员最常犯的10个错误
  16. linux安装桌面xmanager,Linux安装图形界面和Vnc与Xmanager服务
  17. 原码、补码的除法运算
  18. 【JAVA】学习java 基础知识
  19. 正确区分二维动画和三维动画的区别!
  20. 基于GPS经纬度和当地时间计算日落日出时间实现

热门文章

  1. 未来的全能保姆机器人作文_未来的保姆机器人650字作文
  2. 与科大学长孔哥哥~的交流
  3. “泰迪杯”挑战赛 - 基于用户协同过滤算法的电影推荐系统(附算法代码)
  4. Redis 位图数据结构介绍
  5. ifstream fin
  6. java事务占用内存吗,如何排查java应用中CPU使用率高或内存占用高的问题
  7. 快速入门pandas扩展库(上)
  8. 计算机基础考点笔记-1
  9. Revit二次开发,新手接入IExternalCommand、IExternalApplication,如何使用它们!
  10. nyist-部分和问题