functional源码分析

function类的实现中涉及到的类:

  1. _Func_base
    最顶层的基础模板类,定义了函数对象在实例复制、转移上的纯虚接口,如Copy、Move等。同时定义了函数调用的虚接口_Do_call。这些纯虚接口要求在子类中实现。_Func_base的声明:
template<class _Rx,
	class... _Types>
	class _Func_base
	{...}

保存了其关联函数的类型信息,返回值类型Rx,各入参类型列表_Types,这些模板形参在模板类的实现中能够获取到。

  1. _Func_impl
    模板子类,实现了_Func_base定义的所有虚接口。其声明有所不同:
template<class _Callable,
	class _Alloc,
	class _Rx,
	class... _Types>
	class _Func_impl final
		: public _Func_base<_Rx, _Types...>

在模板形参中多了两个参数:
_Callable可调用对对象类型,可是函数指针,也可以是实现operator()操作符的可调用实体。
_Alloc内存分配器,负责_Func_impl对象创建时做内存分配。allocator是STL中非常重要的部分,如容器中元素所用内存空间的分配都是由allocator负责,其对内存获取的方式做了抽象。
A. 可调用对象的保存
在_Func_imp中有如下成员

_Compressed_pair<_Alloc, _Callable> _Mypair;

该成员是一个pair类型,保存内存分配器及可调用类型的对象。_Compressed_pair为类模板,可适配保存任意类型的可调用对象。_Func_impl仅有如下pubic的构造函数,在构造函数中初始化其Pair成员,何时调用该构造函数在后面说明。

	template<class _Other1,
		class _Other2>
		_Func_impl(_Other1&& _Val, _Other2&& _Ax)
		: _Mypair(_One_then_variadic_args_t(),
			_STD forward<_Other2>(_Ax), _STD forward<_Other1>(_Val))
		{	// construct
		}

B. _Func_imp的复制、转移和调用
复制逻辑如下:

	typedef _Func_impl<_Callable, _Alloc, _Rx, _Types...> _Myt;
	...
	// 小内存类型
	template<class _Void>
		_Mybase *_Clone(_Void *_Where, false_type) const
		{	// return clone of *this, small (locally stored)
		_Myalty _Al(_Myax());
		_Myt * _Ptr = static_cast<_Myt *>(_Where);
		_Al.construct(_Ptr, _Callee(), _Myax());
		return (_Ptr);
		}
		
	// 大内存类型
	template<class _Void>
		_Mybase *_Clone(_Void *, true_type) const
		{	// return clone of *this, large (dynamically allocated)
		_Myalty _Al(_Myax());
		_Myt * _Ptr = _Al.allocate(1);

		_TRY_BEGIN
			_Al.construct(_Ptr, _Callee(), _Myax());
		_CATCH_ALL
			_Al.deallocate(_Ptr, 1);
		_RERAISE;
		_CATCH_END

		return (_Ptr);
		}

在指定内存地址位置_Ptr处,分配并初始化_Func_impl对象。实现上对,内存分配和管理做了优化,通过最后一个入参区分大小内存类型:大内存类型由内存分配器动态分配内存,小内存对象则用入参指定的内存。好处在于,通过入参指定的内存地址是预先分配固定内存,以此避免_Func_impl在创建时频繁地申请内存。所以本质上还是新实例的拷贝构造,只不过在内存分配上借助了allocator,隐藏了内存分配细节。这些接口的定义都是为了配合function对象在转移或复制时,作为其内部的成员的_Func_imp完成自身复制。
调用逻辑如下:

	_Callable& _Callee() _NOEXCEPT
		{	// return reference to wrapped function
		return (_Mypair._Get_second()); // !!!返回Pair中保存的Callable对象
		}
    // 入参为右值引用,函数调用时使用foward模板做实参转发
	virtual _Rx _Do_call(_Types&&... _Args)
		{	// call wrapped function
		return (_Invoke_ret(_Forced<_Rx>(), _Callee(),
			_STD forward<_Types>(_Args)...));
		}

其实就是透传参数,间接调用Callable对象。可以推测在function对象调用时会透传给该接口。同时注意的是,其入参是与模板变参列表一致的。

  1. _Func_class
	// TEMPLATE CLASS _Func_class
template<class _Ret,
	class... _Types>
	class _Func_class
		: public _Arg_types<_Types...>
		{
		...
	union _Storage
		{	// storage for small objects (basic_string is small)
		max_align_t _Dummy1;	// for maximum alignment
		char _Dummy2[_Space_size];	// to permit aliasing
		_Ptrt *_Ptrs[_Num_ptrs];	// _Ptrs[_Num_ptrs - 1] is reserved
		};

	_Storage _Mystorage;
		}

_Func_class是最外层的类,_Storage用于保存了_Func_impl对象。内存上使用union,与_Func_impl的复制转移的优化策略相匹配:小内存对象时用&_MyStorage作为预分配内存,否则由分配器动态分配,而且将创建返回的对象地址记录在_Ptrs[_Num_ptrs-1]中。而且注意到_Func_class的模板形参中并没有_Callable和_Alloc,说明该两个形参是在_Func_impl的创建(或拷贝)处通过函数模板形参传入的。关键代码如下:

protected:
	template<class _Fx>
		using _Result_of_invoking_t = result_of_t<_Fx(_Types...)>;

	template<class _Inv_res>
		using _Enable_if_returnable_t = enable_if_t<
			is_convertible<_Inv_res, _Ret>::value || is_void<_Ret>::value>;
	template<class _Fx>
		void _Reset(_Fx&& _Val)
		{	// store copy of _Val
		_Reset_alloc(_STD forward<_Fx>(_Val), allocator<int>());
		}
		
	template<class _Fx,
		class _Alloc>
		void _Reset_alloc(_Fx&& _Val, const _Alloc& _Ax)
		{	// store copy of _Val with allocator
		if (!_Test_callable(_Val))
			{	// null member pointer/function pointer/std::function
			return;	// already empty
			}

		typedef typename decay<_Fx>::type _Decayed;
		typedef _Func_impl<_Decayed, _Alloc, _Ret, _Types...> _Myimpl;
		_Myimpl *_Ptr = 0;

		typedef _Wrap_alloc<_Alloc> _Alimpl0;
		typedef typename _Alimpl0::template rebind<_Myimpl>::other _Alimpl;
		_Alimpl _Al(_Ax);

		_Reset_impl(_STD forward<_Fx>(_Val), _Ax,
			_Ptr, _Al, _Is_large<_Myimpl>());
		}
		
	template<class _Fx,
		class _Alloc,
		class _Myimpl,
		class _Alimpl>
		void _Reset_impl(_Fx&& _Val, const _Alloc& _Ax,
			_Myimpl *, _Alimpl& _Al, true_type)
		{	// store copy of _Val with allocator, large (dynamically allocated)
		_Myimpl *_Ptr = _Al.allocate(1);

		_TRY_BEGIN
			_Al.construct(_Ptr, _STD forward<_Fx>(_Val), _Ax);
		_CATCH_ALL
			_Al.deallocate(_Ptr, 1);
		_RERAISE;
		_CATCH_END

		_Set(_Ptr);
		}

	template<class _Fx,
		class _Alloc,
		class _Myimpl,
		class _Alimpl>
		void _Reset_impl(_Fx&& _Val, const _Alloc& _Ax,
			_Myimpl *, _Alimpl& _Al, false_type)
		{	// store copy of _Val with allocator, small (locally stored)
		_Myimpl *_Ptr = static_cast<_Myimpl *>(_Getspace());
		_Al.construct(_Ptr, _STD forward<_Fx>(_Val), _Ax);
		_Set(_Ptr);
		}
		
	bool _Empty() const _NOEXCEPT
		{	// return true if no stored object
		return (_Getimpl() == 0);
		}
		
	_Ptrt *_Getimpl() const _NOEXCEPT
		{	// get pointer to object
		return (_Mystorage._Ptrs[_Num_ptrs - 1]);
		}

如上接口均为_Func_class中定义的protected接口,后面4)中可以看到function的继承自_Func_class,通过调用这些接口,实现impl对象的创建、释放及状态判断。在impl的创建上,如上几个接口的调用关系为_Reset—— >Reset_alloc——>_Reset_impl,_Cabllable在_Reset调用时传入,分配器默认是allocator。Reset_impl函数模板,接受Callable和Alloc形参类型,并作为_Func_impl模板的实参。整个创建过程与_Func_impl的自身复制逻辑类似,都是先分配内存,然后调用_Func_impl的构造函数做初始化,调用关系如下图。_Func_class是否为空本质上就是判断_Reset_impl对象是否为空。
在这里插入图片描述
4. function

至此,终于看到了我们熟悉的function,其定义如下:

	// TEMPLATE CLASS _Get_function_impl
template<class _Tx>
	struct _Get_function_impl;

#define _GET_FUNCTION_IMPL(CALL_OPT, X1, X2) \
template<class _Ret, \
	class... _Types> \
	struct _Get_function_impl<_Ret CALL_OPT (_Types...)> \
	{	/* determine type from argument list */ \
	typedef _Func_class<_Ret, _Types...> type; \
	};

_NON_MEMBER_CALL(_GET_FUNCTION_IMPL, , )
#undef _GET_FUNCTION_IMPL

	// TEMPLATE CLASS function
template<class _Fty>
	class function
		: public _Get_function_impl<_Fty>::type
	{	// wrapper for callable objects
private:
	typedef typename _Get_function_impl<_Fty>::type _Mybase;

通常fuction的使用不会直接指定返回值和形参列表,而是function<Ret(arg1, arg2,…)>的方式,而_Func_class的模板形参中又是Ret和_Types…,如何转化?这其实就是_Get_function_impl的作用,通过模板偏特化,借助编译器的推导能力,从中分离出Ret和_Types…,构建_Func_class,通俗讲,“返回值”就是_Func_class<_Ret, _Types…>,所以function是继承自_Func_class的。紧接着看一个funciton的构造函数:

	template<class _Fx,
		// 对调用对象的返回做校验,验证是否与function中指定的Ret一致
		class _Inv_res = typename _Mybase::template _Result_of_invoking_t<_Fx&>,
		class = typename _Mybase::template _Enable_if_returnable_t<_Inv_res> >
		function(_Fx _Func)
		{	// construct wrapper holding copy of _Func
		this->_Reset(_STD move(_Func));
		}
		
	function(const _Myt& _Right)
		{	// construct holding copy of _Right
		this->_Reset_copy(_Right);
		}

从构造函数可以看出,实现就是调用基类的_Reset接口,以完成_Func_impl对象的创建和初始化。在拷贝构造中,function的拷贝复制过程就是做_Func_impl对象的拷贝和复制。实现上有一个小的细节,调用函数的签名(返回值和入参)是在类模板的形参列表中指定的,而不是在构造函数function(_Fx _Func)入参类型_Fx中推导出来的,也就是说function对象可以接受任何与template class function中_Fy签名一致或兼容的调用对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值