从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了,这也是泛型编程一个特别让人着迷的地方。