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簽名一致或兼容的調用對象。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章