zoukankan      html  css  js  c++  java
  • C++11中的技术剖析(萃取技术)

    从C++98开始萃取在泛型编程中用的特别多,最经典的莫过于STL。STL中的拷贝首先通过萃取技术识别是否是已知并且支持memcpy类型,如果是则直接通过内存拷贝提高效率,否则就通过类的重载=运算符,相比之下就效率就低了一些。所以说有些做STL优化的程序员为了追求效率就直接改写STL以便于支持可以通过memcpy的结构体,其根本就是利用了C++的萃取识别了自定义结构体。

    C++11增加了移动拷贝,这使得很多时候程序执行效率大幅度提升,与之而来的左值右值总是让初学者摸不清楚头脑,如果遇到各种类型转换只怕是恶心的只想放弃了。但是就我个人而言,因为之前学过苹果的Object-C,曾经一度很羡慕OC中的各种炫酷的功能,但是后来看过C++11,感觉OC有些方面也不外如是。

    闲话到此为止了,这里通过一个万能引用的例子,讲解一下C++11中一部分萃取技术。

    对于函数:

    template<typename T>
    void logAndAdd(T &&t)
    {
    	if (std::is_same<T, int&>::value)
    	{
    		printf("左值引用类型
    ");
    	}
    	else if (std::is_same<T, int>::value)
    	{
    		printf("右值引用类型
    ");
    	}
    }
    

    我们知道,t是一个万能引用类型,因为这里涉及到类型推导,否则的话就是典型的右值引用。对于万能引用,如果传入的是右值,那么通过引用折叠,最终传入的就是T&&类型,如果传入的是左值,那么得到的就是T&类型。

    如果按照以下方式调用上面函数,就会打出相应的结果,具体读者可以自己调试:

    int nA0 = 0;
    
    int &nA1 = nA0;
    logAndAdd(nA1);  // 传入是左值,最终转换成左值引用
    
    logAndAdd(1);   // 传入是右值,最终转换成右值引用
    

    is_same是个什么东西?其实这只是个很简单很简单的模板,实现如下:

    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值的结果决定程序如何运行下去。

    下面再看一个例子

    template<typename T>
    void logAndAddImp(T&& name, std::true_type)
    {
    	printf("logAndAddImp true_type
    ");
    }
    
    template<typename T>
    void logAndAddImp(T&& name, std::false_type)
    {
    	printf("logAndAddImp false_type
    ");
    }
    
    template<typename T>
    void logAndAdd(T &&t)
    {
    	if (std::is_same<std::remove_reference<T>::type, int>::value)
    	{
    		printf("T=int
    ");
    	}
    	else if (std::is_same<std::remove_reference<T>::type, float>::value)
    	{
    		printf("T=float
    ");
    	}
    
    	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
    ");
    	}
    	else if (std::is_same<std::remove_reference<T>::type, float>::value)
    	{
    		printf("T=float
    ");
    	}
    }
    
    
    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
    "
    .text:00401015                 mov     [ebp+nA0], 0
    .text:0040101C                 call    _printf
    .text:00401021                 push    offset _Format  ; "T=int
    "
    .text:00401026                 call    _printf
    .text:0040102B                 mov     [ebp+nA3], 0
    .text:00401032                 push    offset aTFloat  ; "T=float
    "
    .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了,这也是泛型编程一个特别让人着迷的地方。

  • 相关阅读:
    使用rem来开发你的移动端网站
    在网页布局中合理使用inline formating context(IFC)
    构建OLAP CDP平台 Maven父子项目
    2014世界杯决赛观后感
    2013岁末总结
    11月11日上班杂谈
    这一年
    湖南联通发福利了C#为你月赚150M流量回家过年不再愁
    C# 实现对接电信交费易自动缴费 续(winio/winring0 自动填密码)
    C# 实现对接电信交费易自动缴费
  • 原文地址:https://www.cnblogs.com/mod109/p/9236401.html
Copyright © 2011-2022 走看看