使用过STL的人肯定知道,STL有一个适配器的概念,其中stack
, queue
都是适配器,底层使用其他的数据结构来实现。
除了容器有适配器之外,其实函数也提供了适配器,适配器的特点就是将一个类型改装成为拥有子集功能的新的类型。其中函数的适配器典型的就是通过std::bind
来实现。
本文我们看下std::bind
的原理。
在了解std::bind
之前,我们先看一个简单的情况std::bind1st
,这个函数相当我们可以绑定第一个值,可以将一个二元操作转换为一元操作。
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
.
std::bind1st
原理其实这个函数是非常简单的,返回一个对象给调用者,这个对象有如下特性:
result_type operator()(const argument_type& _Right)
.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 operands
return (op(value, _Right));
}
result_type operator()(argument_type& _Right) const
{ // apply functor to operands
return (op(value, _Right));
}
protected:
_Fn2 op; // the functor to apply
typename _Fn2::first_argument_type value; // the left operand
};
// TEMPLATE FUNCTION bind1st
template<class _Fn2,
class _Ty> inline
binder1st<_Fn2> bind1st(const _Fn2& _Func, const _Ty& _Left)
{ // return a binder1st functor adapter
typename _Fn2::first_argument_type _Val(_Left);
return (binder1st<_Fn2>(_Func, _Val));
}
从上面代码我们可以很简单的发现:
binder1st
.typename _Fn2::first_argument_type value;
保存了绑定的值。_Fn2 op;
定义了被适配的函数。result_type operator()(argument_type& _Right)
.return (op(value, _Right));
.与之类似,还存在一个绑定第二个参数的适配器为std::bind2nd
,这个函数适配器的原理和std::bind1st
类似,这里不再分析。
标准库中,认为一元操作和二元操作是非常常用的算法操作,但是三元以及以上是极少的,因此标准库中并没有定义三元函数和其适配器。
但是提供了更加通用的东西std::bind
,来绑定参数信息。
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
的具体实现原理。
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 type
return (_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 \
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
};
这个实现过程如下:
decay_t<_Fx> _First
: 这个声明一个Binder
函数的类型。tuple...> _Second
: 绑定的参数的元组。_Compressed_pair<_First, _Second> _Mypair
将函数和参数元组组成一个pair。auto 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
的执行过程:
_Mypair._Get_first()
: 获取到函数对象。_Mypair._Get_second()
: 获取绑定的参数列表。forward_as_tuple(_STD forward<_Unbound>(_Unbargs)...))
: 参数元组。_Call_binder
的实现如下:
template<class _Ret,
size_t... _Ix,
class _Cv_FD,
class _Cv_tuple_TiD,
class _Untuple> inline
auto _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() invocation
return (_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;
}
从上来看,std::bind
其实就是一个函数的适配器,用来绑定其中的参数的特定值,实现了一个特化版本的函数。