從c++標(biāo)準(zhǔn)庫指針萃取器談一下traits技法(推薦)
本篇文章基于gcc中標(biāo)準(zhǔn)庫源碼剖析一下標(biāo)準(zhǔn)庫中的模板類pointer_traits
,并且以此為例理解一下traits技法。
說明一下,我用的是gcc7.1.0編譯器,標(biāo)準(zhǔn)庫源代碼也是這個版本的。
還是先看一下思維導(dǎo)圖,如下:
1. 指針萃取器pointer_traits說明
首先說明一下哈,官方并沒有指針萃取器這個名稱,其實pointer_traits
是類模板,它是c++11以后引入的,可以通過傳入的重綁定模板類型得到相應(yīng)的指針類型,比較官方的描述是:pointer_traits
類模板提供標(biāo)準(zhǔn)化方法,用于訪問類指針類型的某些屬性。
那么為什么要把這個pointer_traits
拿出來單獨說明一下呢,因為類似之前的內(nèi)存分配器一樣,它是stl中某些容器的使用前提,在講容器的時候,繞不開它,所以先把它搞清楚了有助于后續(xù)的學(xué)習(xí)和理解。
為什么要叫指針萃取器呢,我理解它類似于內(nèi)存萃取器allocator_traits
,都是根據(jù)模板參數(shù)去得到某種類型,并且traits也有萃取的意思,所以我這里就叫指針萃取器了。
2. 指針萃取器源代碼分析
類模板pointer_traits
在標(biāo)準(zhǔn)庫中有兩個版本,一個特化版本,一個非特化版本,源代碼都在bits/ptr_traits.h
頭文件中,當(dāng)然實際使用的時候它是被包含在頭文件memory
中的。
2.1 非特化pointer_traits
我們先分析一下非特化版本的源代碼,如下:
//pointer_traits類模板 template<typename _Ptr> struct pointer_traits { private: template<typename _Tp> using __element_type = typename _Tp::element_type; template<typename _Tp> using __difference_type = typename _Tp::difference_type; template<typename _Tp, typename _Up, typename = void> struct __rebind : __replace_first_arg<_Tp, _Up> { }; //如果__void_t參數(shù)里面類型存在則直接使用下面這個結(jié)構(gòu)體,否則使用上面那個 template<typename _Tp, typename _Up> struct __rebind<_Tp, _Up, __void_t<typename _Tp::template rebind<_Up>>> { using type = typename _Tp::template rebind<_Up>; }; public: using pointer = _Ptr; using element_type = __detected_or_t<__get_first_arg_t<_Ptr>, __element_type, _Ptr>; using difference_type = __detected_or_t<ptrdiff_t, __difference_type, _Ptr>; template<typename _Up> using rebind = typename __rebind<_Ptr, _Up>::type; static _Ptr pointer_to(__make_not_void<element_type>& __e) { return _Ptr::pointer_to(__e); } static_assert(!is_same<element_type, __undefined>::value, "pointer type defines element_type or is like SomePointer<T, Args>"); };
對于這段代碼,其實初看起來是有點懵的,但是萬變不離其宗,一個類被定義出來,最后是給別人使用的,所以對于類類型而言,我們只要搞懂它的公共成員都有些什么作用,那大概也就知道這個類的作用了。
這里需要說明一下__detected_or_t
的作用,它也是一個類型模板,聲明如下:
template<typename _Default, template<typename...> class _Op, typename... _Args> using __detected_or_t = typename __detected_or<_Default, _Op, _Args...>::type;
作用是如果_Op<_Args...>
是一個有效的類型,那這個類型就是_Op<_Args...>
,否則就是_Default
。
那么對于類模板pointer_traits
,它的公共成員作用如下:
- pointer,這個其實就是模板參數(shù)
_ptr
的一個別名; - element_type,也是一個別名,如果
_ptr::element_type
這個類型存在,則它就是_ptr::element_type
這個類型,如果_ptr::element_type
這個類型不存在,但是_ptr
是一個模板特化,則它就是_ptr
,否則就是__undefined
,其實就是無意義類型了; - difference_type,也是一個別名,如果
_ptr::difference_type
這個類型存在,則它就是_ptr::difference_type
,否則就是ptrdiff_t
類型; - template<typename _Up>using rebind,它是一個類型別名模板,由類
pointer_traits
的模板參數(shù)和rebind的模板參數(shù)一起決定最終到底是什么類型,若_ptr::rebind<_Up>
這個類型存在則它就是_ptr::rebind<_Up>
,否則根據(jù)類型模板__replace_first_arg
的實現(xiàn),若_ptr
是模板特化_Template<_Tp, _Types...>
,則它是_Template<_Tp, _Types...>
,否則就沒有類型; - pointer_to,它是一個靜態(tài)成員函數(shù),調(diào)用模板類型的pointer_to函數(shù),所以具體什么作用取決于
_ptr
的實現(xiàn),但根據(jù)字面意思應(yīng)該是獲取element_type
類型對象的地址。
所以總的來看,說白了類模板pointer_traits
其實就是用于獲取模板參數(shù)_ptr
的某些類型屬性,那從這里反推一下,也能知道這個模板參數(shù)類型需要具有一些什么屬性。
2.2 特化pointer_traits
接下來看一下特化類模板pointer_traits
的源代碼實現(xiàn):
template<typename _Tp> struct pointer_traits<_Tp*> { typedef _Tp* pointer; //為特化類型取個別名 typedef _Tp element_type; //為模板類型取別名 typedef ptrdiff_t difference_type; template<typename _Up> using rebind = _Up*; static pointer pointer_to(__make_not_void<element_type>& __r) noexcept { return std::addressof(__r); } };
對于特化類型,它的公共成員與非特化其實是一致的,只是它是為_Tp*
類型提供的特化,對于其他公共成員,這里比較簡單,就不再多說了,重點再看一下template<typename _Up> using rebind
這個類型別名模板,它直接獲取一個_Up*
類型的指針,結(jié)合整體來看,它的作用就是:重綁定類型成員模板別名,使得可以由指向 _Tp
的指針類型,獲取指向 _Up
的指針類型。
源代碼分析完以后,貌似有點印象了,但是我們具體應(yīng)該怎么使用呢?
3. 指針萃取器的簡單使用
我們先寫一段例子代碼,如下:
#include <memory> #include <iostream> #include <typeinfo> #include <cxxabi.h> //將gcc編譯出來的類型翻譯為真實的類型 const char* GetRealType(const char* p_szSingleType) { const char* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr, nullptr, nullptr); return szRealType; } int main() { using ptr = typename std::pointer_traits<int*>::template rebind<double>; ptr p1; const std::type_info &info = typeid(p1); std::cout << GetRealType(info.name()) << std::endl; return 0; }
上面這個例子很顯然用到了特化的pointer_traits
,并且用的rebind屬性,由指向int的指針類型獲得了指向double的指針類型,代碼輸出如下:
double*
看上面的代碼,我們還是不知道pointer_traits
到底有啥作用,并且看起來是把簡單的類型搞復(fù)雜了,但有一點,當(dāng)我們不知道確切類型的時候,使用這個標(biāo)準(zhǔn)模板類獲取指針類型還是蠻方便的,這一點在標(biāo)準(zhǔn)庫的deque
容器中就有使用。
而對于非特化的pointer_traits
,看一下下面這段代碼:
#include <memory> #include <iostream> #include <typeinfo> #include <cxxabi.h> #include <string> struct test_traits { using element_type = int; using difference_type = double; }; struct test_traits2 { using element_type = std::string; using difference_type = size_t; }; const char* GetRealType(const char* p_szSingleType) { const char* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr, nullptr, nullptr); return szRealType; } int main() { using type1 = typename std::pointer_traits<test_traits>::element_type; using type2 = typename std::pointer_traits<test_traits2>::difference_type; const std::type_info &info = typeid(type1); std::cout << GetRealType(info.name()) << std::endl; const std::type_info &info2 = typeid(type2); std::cout << GetRealType(info2.name()) << std::endl; return 0; }
說白了,從這里看pointer_traits
的作用就是得到某些類型的屬性,這個在類型未知的時候就比較有用,比較典型的用法是在標(biāo)準(zhǔn)庫的allocator_traits
類模板里面,我們之前說過,allocator_traits
是內(nèi)存萃取器,在這個萃取器里面,會通過pointer_traits
獲取一些分配器的類型屬性。
4. 從指針萃取器角度談traits技法
所謂traits
,字面意思是特性、特征,所以說白了,traits技法其實就是獲取未知類型的某些屬性,為什么說是未知,因為traits主要用于模板編程中,根據(jù)模板類型去獲取某些類型特性,如果是已知的類型,那就沒有必要使用traits技法了。
比如本篇文章所講的pointer_traits
,它就是使用traits技法的典型案例,按照字面意思我們可以理解為指針的特性,所以非特化的pointer_traits
它就是用于獲取某些類指針的類型特性,而一般特化的pointer_traits
其實是用于原生指針類型,比如int*
這樣的。
下面我們再看一看怎么使用非特化的pointer_traits
獲取類指針的特性,如下:
#include <memory> #include <iostream> #include <typeinfo> #include <cxxabi.h> const char* GetRealType(const char* p_szSingleType) { const char* szRealType = abi::__cxa_demangle(p_szSingleType, nullptr, nullptr, nullptr); return szRealType; } int main() { using type = typename std::pointer_traits<std::shared_ptr<int>>::element_type; const std::type_info &info = typeid(type); std::cout << GetRealType(info.name()) << std::endl; return 0; }
代碼輸出:int
,它獲取了智能指針的element_type
特性。
到此這篇關(guān)于從c++標(biāo)準(zhǔn)庫指針萃取器談一下traits技法的文章就介紹到這了,更多相關(guān)c++ traits技法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C/C++實現(xiàn)數(shù)字與字符串互相轉(zhuǎn)換的多種方法
在C/C++程序中,會需要把數(shù)字與字符串做出互相轉(zhuǎn)換的操作,用于實現(xiàn)程序想要的效果,下面將介紹多種方法實現(xiàn)數(shù)字與字符串互相轉(zhuǎn)換,文中有詳細的代碼示例供大家參考,需要的朋友可以參考下2024-08-08C++中volatile關(guān)鍵字及常見的誤解總結(jié)
這篇文章主要給大家介紹了關(guān)于C++中volatile關(guān)鍵字及常見的誤解的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05淺談C++中thread庫join和detach的區(qū)別
這篇文章主要為大家介紹了C++中thread庫join和detach的區(qū)別,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2021-11-11c語言獲取當(dāng)前工作路徑的實現(xiàn)代碼(windows/linux)
這篇文章主要介紹了c語言獲取當(dāng)前工作路徑的實現(xiàn)代碼(windows/linux),需要的朋友可以參考下2017-09-09cocos2d-x學(xué)習(xí)筆記之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMu
這篇文章主要介紹了cocos2d-x學(xué)習(xí)筆記之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex場景層介紹,需要的朋友可以參考下2014-09-09