C++ bind浅析

文章目录

  • 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 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));
	}

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

  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 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
	};

这个实现过程如下:

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

所以结构信息如下:
C++ bind浅析_第1张图片

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> 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))...));
	}

参数信息如下:
C++ bind浅析_第2张图片

调用函数的时候,会将_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++语言编程,c++)