【導讀】:本文主要講解C++ function技術的實現(xiàn)與具體運用。
std::function是一個函數(shù)對象的包裝器,std::function的實例可以存儲,復制和調(diào)用任何可調(diào)用的目標,包括:
函數(shù)。
lamada表達式。
綁定表達式或其他函數(shù)對象。
指向成員函數(shù)和指向數(shù)據(jù)成員的指針。
當std::function對象沒有初始化任何實際的可調(diào)用元素,調(diào)用std::function對象將拋出std::bad_function_call異常。
本文我們來談一下std::function的實現(xiàn)原理。
1. std::function簡介
在討論其原理的時候,我們來熟悉一下這個東西是怎么使用的,C++標準庫詳細說明了這個的基本使用http://www.cplusplus.com/reference/functional/function/.
這里我們大概總結一下。
1.1 Member types
result_type | 返回類型 |
argument_type | 如果函數(shù)對象只有一個參數(shù),那么這個代表參數(shù)類型。 |
first_argument_type | 如果函數(shù)對象有兩個個參數(shù),那么這個代表第一個參數(shù)類型。 |
second_argument_type | 如果函數(shù)對象有兩個個參數(shù),那么這個代表第二個參數(shù)類型。 |
成員類型 | 說明 |
---|
1.2 Member functions
constructor | 構造函數(shù):constructs a new std::function instance |
destructor | 析構函數(shù):destroys a std::function instance |
operator= | 給定義的function對象賦值 |
operator bool | 檢查定義的function對象是否包含一個有效的對象 |
operator() | 調(diào)用一個對象 |
成員函數(shù)聲明 | 說明 |
---|
1.3 基本使用
#include
從上面我們可以發(fā)現(xiàn),std::function可以表示函數(shù),lamada,可調(diào)用類對象。
2. std::function實現(xiàn)
在標準庫中STL設計為如下:
template
上面的std::function繼承關系比較簡單,主要使用
union_Storage { //storageforsmallobjects(basic_stringissmall) max_align_t_Dummy1;//formaximumalignment char_Dummy2[_Space_size];//topermitaliasing _Ptrt*_Ptrs[_Num_ptrs];//_Ptrs[_Num_ptrs-1]isreserved };
這個來存儲我們設置的可調(diào)用對象,我們從std::function的使用過程看一下整個原理。
2.1 函數(shù)對象賦值
我們使用的時候一般使用f = Caller;來設置函數(shù)對象,我們看下這個的實現(xiàn)過程。
template
我們看this->_Reset(_Func)這個函數(shù),因為這個才是設置函數(shù)可調(diào)用對象的東西。
void_Set(_Ptrt*_Ptr)_NOEXCEPT {//storepointertoobject _Mystorage._Ptrs[_Num_ptrs-1]=_Ptr; } void_Reset_impl(_Fx&&_Val,const_Alloc&_Ax, _Myimpl*,_Alimpl&_Al,false_type) {//storecopyof_Valwithallocator,small(locallystored) _Myimpl*_Ptr=static_cast<_Myimpl?*>(_Getspace()); _Al.construct(_Ptr,_STDforward<_Fx>(_Val),_Ax); _Set(_Ptr); } template
這個代碼的主要意思就是創(chuàng)建一個_Func_impl<_Decayed, _Alloc, _Ret, _Types...>指針,然后賦值_Mystorage._Ptrs[_Num_ptrs - 1] = _Ptr;。
設置之后,我們看下調(diào)用操作怎么完成。
2.2 operator() 的實現(xiàn)
調(diào)用操作主要是通過operator()來實現(xiàn)的,我們看下這個的實現(xiàn)過程。
_Ptrt*_Getimpl()const_NOEXCEPT {//getpointertoobject return(_Mystorage._Ptrs[_Num_ptrs-1]); } _Retoperator()(_Types..._Args)const {//callthroughstoredobject if(_Empty()) _Xbad_function_call(); return(_Getimpl()->_Do_call(_STDforward<_Types>(_Args)...)); }
因此,我們是通過_Func_impl<_Decayed, _Alloc, _Ret, _Types...>轉(zhuǎn)發(fā)了調(diào)用操作_Do_call
2.3 _Func_impl的實現(xiàn)
class_Func_impl :public_Func_base<_Rx,?_Types...> {//derivedclassforspecificimplementationtypes public: typedef_Func_impl<_Callable,?_Alloc,?_Rx,?_Types...>_Myt; typedef_Func_base<_Rx,?_Types...>_Mybase; typedef_Wrap_alloc<_Alloc>_Myalty0; typedeftypename_Myalty0::templaterebind<_Myt>::other_Myalty; typedefis_nothrow_move_constructible<_Callable>_Nothrow_move; virtual_Rx_Do_call(_Types&&..._Args) {//callwrappedfunction return(_Invoke_ret(_Forced<_Rx>(),_Callee(), _STDforward<_Types>(_Args)...)); } _Compressed_pair<_Alloc,?_Callable>_Mypair; };
_Func_impl這個類通過_Do_call來轉(zhuǎn)發(fā)函數(shù)對象的調(diào)用操作。
3. 總結
這里我們看下std::function的結構信息,如下:
從這里我們發(fā)現(xiàn)_Storage大小為:
constint_Num_ptrs=6+16/sizeof(void*); constsize_t_Space_size=(_Num_ptrs-1)*sizeof(void*);
_Num_ptrs值為10。
如果我們賦值的對象有成員變量會是什么情況呢?例如如下:
classCCaller { public: intoperator()(inta,intb,intc,intd) { std::cout<f; f=Caller; f(10,20,30,40); return0; }
內(nèi)存結構如下:
由此我們可以發(fā)現(xiàn)std::function是利用一個_Compressed_pair<_Alloc, _Callable> _Mypair;拷貝了元素的數(shù)據(jù)信息。
主要的初始化過程為:
emplate
其中decay<_Fx>::type定義了_Compressed_pair<_Alloc, _Callable> _Mypair;中_Callable的類型,這個聲明如下(也就是去掉引用和其他屬性信息):
template
至此,我們大致上完成了std::function的原理分析了,希望在后續(xù)的使用中,我們能夠知道std::function在什么情況下可以使用,以及背后完成的事情。
責任編輯:lq
-
函數(shù)
+關注
關注
3文章
4327瀏覽量
62573 -
C++
+關注
關注
22文章
2108瀏覽量
73622 -
初始化
+關注
關注
0文章
50瀏覽量
11850
原文標題:C++ std::function 技術淺談
文章出處:【微信號:LinuxHub,微信公眾號:Linux愛好者】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論