从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("左值引用类型\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是个什么东西?其实这只是个很简单很简单的模板,实现如下:

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

C++11中的技术剖析(萃取技术)的更多相关文章

  1. C++的类型萃取技术

    应该说,迭代器就是一种智能指针,因此,它也就拥有了一般指针的所有特点——能够对其进行*和->操作.但是在遍历容器的时候,不可避免的要对遍历的容器内部有所了解,所以,设计一个迭代器也就自然而然的变 ...

  2. C++萃取技术的一个简单初探

    首先本文并不是讲解C++萃取技术,关于C++的萃取技术网上有很多文章,推荐http://www.cppblog.com/woaidongmao/archive/2008/11/09/66387.htm ...

  3. CGI 萃取技术 __type_traits

    前言 上一篇文章讲了 iterator_traits 的编程技法,非常棒适度弥补了 C++ 语言的不足. STL 只对迭代器加以规范,制定了 iterator_traits 这样的东西. CGI 把这 ...

  4. C++之萃取技术(traits)

    为什么需要类型萃取(特化) 前面我们提到了迭代器,它是一个行为类似于smart pointer之类的东西,主要用于对STL容器中的对象进行访问,而且不暴露容器中的内部结构,而迭代器所指对象的型别称为该 ...

  5. c++11——type_traits 类型萃取

    一. c++ traits traits是c++模板编程中使用的一种技术,主要功能:     把功能相同而参数不同的函数抽象出来,通过traits将不同的参数的相同属性提取出来,在函数中利用这些用tr ...

  6. STL源代码分析--萃取编程(traits)技术的实现

    1.为什么要出现? 依照默认认定.一个模板给出了一个单一的定义,能够用于用户能够想到的不论什么模板參数!可是对于写模板的人而言,这样的方式并不灵活.特别是遇到模板參数为指针时,若想实现与类型的參量不一 ...

  7. 类型萃取(type traits)

    1. 类型萃取的作用 类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断该类型是否含有某些特性,从而在泛型算法中来对该类型进行特殊的处理用来提高效率或者其他.例如:在STL ...

  8. WCF技术剖析之二十: 服务在WCF体系中是如何被描述的?

    原文:WCF技术剖析之二十: 服务在WCF体系中是如何被描述的? 任何一个程序都需要运行于一个确定的进程中,进程是一个容器,其中包含程序实例运行所需的资源.同理,一个WCF服务的监听与执行同样需要通过 ...

  9. WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用

    原文:WCF技术剖析之十五:数据契约代理(DataContractSurrogate)在序列化中的作用 [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经> ...

随机推荐

  1. 39th 迷迷糊糊 二豆玩不转了

    今天学的语法 1.   #  {} . format()的传送作用 请从键盘获取一个整数,求他的平方根,要求: 1 如果这个整数是大于等于0,则直接打印其平方根 2 否则, 打印其绝对值的平方根 x ...

  2. Centos7.2安装MariaDB数据库,并进行基础配置

    [注] MariaDB的安装与配置感谢博主carlo-jie的分享,原博文地址https://www.cnblogs.com/carlo-jie/p/6104135.html. 第二小节:用户创建及权 ...

  3. Jmeter实现百分比业务比例

    Jmeter实现百分比业务比例   相较于LoadRunner,jmeter在复杂场景方式貌似略有欠缺.前一段时间,想实现一个功能,如有两个采样器a与b,a采样器与b采样器被执行的概率分别为1/4与3 ...

  4. 6380. 【NOIP2019模拟2019.10.06】小w与最长路(path)

    题目 题目大意 给你一棵树,对于每一条边,求删去这条边之后,再用一条边(自己定)连接两个连通块,形成的树的直径最小是多少. 正解 首先,将这棵树的直径给找出来.显然,如果删去的边不在直径上,那么答案就 ...

  5. PHP FILTER_SANITIZE_STRIPPED 过滤器

    定义和用法 FILTER_SANITIZE_STRIPPED 过滤器去除或编码不需要的字符. 该过滤器是 FILTER_SANITIZE_STRING 过滤器的别名 该过滤器删除那些对应用程序有潜在危 ...

  6. Android中的ListView的绘制过程中执行的方法

    首先,系统在绘制ListView之前, 将会先调用getCount方法来获取Item的个数.(如果getCount方法返回0的话,列表时不显示任何内容的) 之后每绘制一个 Item就会调用一次getV ...

  7. Hbase表类型的设计

    HBase表类型的设计 1.短宽 这种设计一般适用于: * 有大量的列 * 有很少的行 2.高瘦 这种设计一般适用于: * 有很少的列 * 有大量的行 3.短宽-高瘦的对比 短宽 * 使用列名进行查询 ...

  8. HBase与Hive交互操作案例

    HBase与Hive交互操作 1.环境准备 因为我们后续可能会在操作Hive的同时对HBase也会产生影响,所以Hive需要持有操作HBase的Jar,那么接下来拷贝Hive所依赖的Jar包(或者使用 ...

  9. 前端使用vue-i18n做中英文翻译

    vue-i18n 仓库地址:https://github.com/kazupon/vue-i18n 兼容性: 支持 Vue.js 2.x 以上版本 安装方法:(此处只演示 npm) npm insta ...

  10. 7、Appium常用API

    嗯,官网已经介绍的很全了.会选几个常用API后期整理. Appium常用API地址:http://appium.io/docs/cn/writing-running-appium/appium-bin ...