從C++98開始萃取在泛型編程中用的特別多,最經典的莫過於STL。STL中的拷貝首先經過萃取技術識別是不是已知而且支持memcpy類型,若是是則直接經過內存拷貝提升效率,不然就經過類的重載=運算符,相比之下就效率就低了一些。因此說有些作STL優化的程序員爲了追求效率就直接改寫STL以便於支持能夠經過memcpy的結構體,其根本就是利用了C++的萃取識別了自定義結構體。程序員
C++11增長了移動拷貝,這使得不少時候程序執行效率大幅度提高,與之而來的左值右值老是讓初學者摸不清楚頭腦,若是遇到各類類型轉換隻怕是噁心的只想放棄了。可是就我我的而言,由於以前學過蘋果的Object-C,曾經一度很羨慕OC中的各類炫酷的功能,可是後來看過C++11,感受OC有些方面也不外如是。編程
閒話到此爲止了,這裏經過一個萬能引用的例子,講解一下C++11中一部分萃取技術。cookie
對於函數:函數
template<typename T> void logAndAdd(T &&t) { if (std::is_same<T, int&>::value) { printf("左值引用類型\r\n"); } else if (std::is_same<T, int>::value) { printf("右值引用類型\r\n"); } }
咱們知道,t是一個萬能引用類型,由於這裏涉及到類型推導,不然的話就是典型的右值引用。對於萬能引用,若是傳入的是右值,那麼經過引用摺疊,最終傳入的就是T&&類型,若是傳入的是左值,那麼獲得的就是T&類型。優化
若是按照如下方式調用上面函數,就會打出相應的結果,具體讀者能夠本身調試:調試
int nA0 = 0; int &nA1 = nA0; logAndAdd(nA1); // 傳入是左值,最終轉換成左值引用 logAndAdd(1); // 傳入是右值,最終轉換成右值引用
is_same是個什麼東西?其實這只是個很簡單很簡單的模板,實現以下:orm
template<class _Ty1,class _Ty2> struct is_same : false_type { }; template<class _Ty1> struct is_same<_Ty1, _Ty1> : true_type { }; template<class _Ty,_Ty _Val> struct integral_constant { static constexpr _Ty value = _Val; typedef _Ty value_type; typedef integral_constant<_Ty, _Val> type; constexpr operator value_type() const _NOEXCEPT { return (value); } constexpr value_type operator()() const _NOEXCEPT { return (value); } }; typedef integral_constant<bool, true> true_type; typedef integral_constant<bool, false> false_type;
從中能夠看出,_Ty1和_Ty2相等時構造的則是第二個結構體,反之則是第一個結構體。而所謂的返回值則是true_type或者false_type。當std::is_same<T, int&>其值爲true_type時,其實就是構造了一個integral_constant<bool, true>臨時對象,而std::is_same<T, int&>::value的本質無非就是integral_constant<bool, true>構造的這個臨時對象中取出value這個值,而value在本例中的定義就是static constexpr _Ty value = _Val;其中_Ty爲bool型。對象
也就是說std::is_same<T, int&>::value只是經過T, int&類型對比是否一致,而後根據結果構造了一個臨時對象,經過這個對象賦予初始類型和數值<bool, true>,從而返回了一個bool類型的值,再經過這個bool值的結果決定程序如何運行下去。blog
下面再看一個例子內存
template<typename T> void logAndAddImp(T&& name, std::true_type) { printf("logAndAddImp true_type\r\n"); } template<typename T> void logAndAddImp(T&& name, std::false_type) { printf("logAndAddImp false_type\r\n"); } template<typename T> void logAndAdd(T &&t) { if (std::is_same<std::remove_reference<T>::type, int>::value) { printf("T=int\r\n"); } else if (std::is_same<std::remove_reference<T>::type, float>::value) { printf("T=float\r\n"); } logAndAddImp(std::forward<T>(t), std::is_integral<typename std::remove_reference<T>::type>()); }
這裏首先說一下std::is_integral,從字面意義上說,這裏就是和以前判斷是否同一類型同樣。可是判斷首先會remove_reference移除原來類型上的引用屬性,const屬性和volatile屬性。也就是說,無論是int類型,int*,仍是const int都會被判斷成int類型。源碼很簡單以下(由於篇幅,這裏只複製一部分)
template<class _Ty> struct _Is_integral: false_type { }; template<> struct _Is_integral<char32_t>: true_type { }; template<> struct _Is_integral<_LONGLONG>: true_type { }; template<> struct _Is_integral<_ULONGLONG>: true_type { };
true_type和false_type其實和以前同樣,而
std::is_integral<typename std::remove_reference<T>::type>()最終獲得的結果,也和以前is_same同樣,是一個bool型的變量。可是從這裏能夠看到,只要是_Is_integral特化過的類型都會返回true,不然就爲假。
這類萃取在實際代碼中很是之高效,以VS2015爲例,編譯如下代碼:
template<typename T> void logAndAdd(T &&t) { if (std::is_same<std::remove_reference<T>::type, int>::value) { printf("T=int\r\n"); } else if (std::is_same<std::remove_reference<T>::type, float>::value) { printf("T=float\r\n"); } } int main() { const int i = 0; int nA0 = 0; //logAndAdd(nA0); int &nA1 = nA0; logAndAdd(nA1); logAndAdd(1); const int &nA2 = 0; logAndAdd(nA2); volatile int nA3 = 0; logAndAdd(nA3); float t = 0.1f; logAndAdd(t); getchar(); return 0; }
最終獲得的release版本exe,反彙編以下所示:
.text:00401000 ; int __cdecl main() .text:00401000 _main proc near ; CODE XREF: __scrt_common_main_seh+F4p .text:00401000 .text:00401000 nA0 = dword ptr -0Ch .text:00401000 nA3 = dword ptr -8 .text:00401000 var_4 = dword ptr -4 .text:00401000 .text:00401000 push ebp .text:00401001 mov ebp, esp .text:00401003 sub esp, 0Ch .text:00401006 mov eax, ___security_cookie .text:0040100B xor eax, ebp .text:0040100D mov [ebp+var_4], eax .text:00401010 push offset _Format ; "T=int\r\n" .text:00401015 mov [ebp+nA0], 0 .text:0040101C call _printf .text:00401021 push offset _Format ; "T=int\r\n" .text:00401026 call _printf .text:0040102B mov [ebp+nA3], 0 .text:00401032 push offset aTFloat ; "T=float\r\n" .text:00401037 mov [ebp+nA3], 0 .text:0040103E call _printf .text:00401043 add esp, 0Ch .text:00401046 call ds:__imp__getchar .text:0040104C mov ecx, [ebp+var_4] .text:0040104F xor eax, eax .text:00401051 xor ecx, ebp ; cookie .text:00401053 call @__security_check_cookie@4 ; __security_check_cookie(x) .text:00401058 mov esp, ebp .text:0040105A pop ebp .text:0040105B retn .text:0040105B _main endp
沒有任何判斷邏輯,純粹是所有被優化,提取出來須要打印的地方直接printf了,這也是泛型編程一個特別讓人着迷的地方。