模版元编程之——Type Traits
来源:互联网 发布:网络销售授权书模板 编辑:程序博客网 时间:2024/06/10 01:45
元编程之中有两种元数据,一种是类型数据,这里的type traits就是对类型数据的操作或者称为计算。这种技术在STL的设计中使用的非常广泛。本质上是借用C++模版提供的模版形参推导和特化两种机制来实现的。
迭代器
迭代器是联系算法与容器的桥梁,二者彼此独立设计,都使用泛型方法实现。迭代器就是联系他们的胶着剂。以经典的交换函数swap来入手:这个函数是一个模版函数,接收两个迭代器模版参数,将他们所指之物进行交换。
为了得到迭代器所指之物的类型,就需要使用了模版参数的自动推导来实现,因为C++不提供typeof操作,typeid也只能获取类型名称而不能拿来当做变量声明使用。
为了实现异常安全性代码,通常的实现方式是copy-and-swap方式,代码如下:
template<typename Ite1, typename Ite2, typename V>void swap(Ite1 i1, Ite2 i2, V){ V tmp(*i1); *i1 = *i2; *i2 = tmp;}//调用方式list<int> a; vector<int> b;swap(a.begin(), b.begin(), *a);
交换的两个迭代器所指的类型必须相同,上述调用中都是指向int类型的迭代器,迭代器类型可以不同也可以相同。从上述例子中可以看出,swap的第三个模版参数V就是用来进行tmp变量声明使用,并无其他作用,这正是利用了模版参数自动推导实现的。
但是,上述模版函数实现的第三个参数太丑陋。因此,最好将这个类型放到迭代器内部,只需要传入两个迭代器给swap函数。这正是type traits(类型萃取)的来历。
类型萃取
将上述实现代码进行改进,得到如下的经过萃取后的代码:
template<typename Ite>struct iterator_traits{ typedef typename Ite::value_type value_type; //其他萃取类型,如reference,pointer等...};template<typename Ite1, typename Ite2>void swap(Ite1 i1, Ite2 i2){ typename iterator_traits<Ite1>::value_type tmp(*i1); *i1 = *i2; *i2 = tmp;}
上述实现中的iterator_traits就是一个萃取迭代器类型的包装模板类,它要求每个迭代器自己定义内嵌的typedef类型定义,支持value_type
、reference等多种类型。
可以看出,iterator_traits
仅仅是将各个迭代器自定义的内嵌类型做了一次转接或者包装,我们可以不用这个包装,而直接使用迭代器的内嵌类型也是可以的:
template<typename Ite1, typename Ite2>void swap(Ite1 i1, Ite2 i2){ typename Ite1::value_type tmp(*i1); *i1 = *i2; *i2 = tmp;}
但是,对于原生指针、引用等类型,是无法使用上述方式进行的,这就体现了使用iterator_traits
包装的作用,因为模板类可以进行偏特化,专门针对引用、指针等实现特化版本。
template<typename Ite>struct iterator_traits<Ite*>{ //针对原生指针的偏特化 typedef Ite value_type; typedef Ite * pointer; typedef Ite & reference; typedef ptrdiff_t difference_type; //...};template<typename Ite>struct iterator_traits<const Ite*>{ //针对常量指针的偏特化 typedef Ite value_type; typedef Ite * pointer; typedef Ite & reference; typedef ptrdiff_t difference_type; //...};
高效swap
除了前面所述的对迭代器所指类型的萃取之外,还有一个问题就是,如果某些迭代器所指类型是很大的对象,创建一个临时变量并进行拷贝会非常耗时,需要进行动态内存分配和释放。这里就有一个是否是通过pimpl手法实现的问题,如果是就只需要交换简单的指针而不用创建对象,而交换指针只需要用标准库提供的std::swap函数进行高效交换即可。因此,这里需要使用偏特化来实现条件选择,如果两个迭代器类型相同,而且是所指类型是指针或者引用就可以进行高效的交换。
首先是判断迭代器类型以及所指类型,这就是用偏特化来实现if-else语句。
template<typename T, typename U>struct is_same{ const static int value = 0;};template<typename T>struct is_same<T, T>{ const static int value = 1;};template<typename T>struct is_pointer{ const static int value = 0;};template<typename T>struct is_pointer<T *>{ const static int value = 1;};//is_reference与上述实现类似
这样就可以对swap模版函数进行高效实现,其中对使用两个版本的函数又使用全特化进行了一次转发。
template<typename Ite1, typename Ite2>void swap(Ite1 i1, Ite2 i2){ typedef typename iterator_traits<Ite1> itetraits1; typedef typename iterator_traits<Ite2> itetraits2; typedef typename itetraits1::value_type v1; typedef typename itetraits1::reference_type r1; typedef typename itetraits1::value_type v1; typedef typename itetraits1::reference_type r2; const bool tag = is_same<v1, v2> && is_reference<r1> && is_reference<r2>; swap_impl<tag>(i1, i2);}template<bool tag>struct swap_impl{ template<typename Ite1, typename Ite2> swap_impl(Ite1 i1, Ite2, i2){ typename iterator_traits<Ite1>::value_type tmp(*i1); *i1 = *i2; *i2 = tmp; }};template<>struct swap_impl<true>{ template<typename Ite1, typename Ite2> swap_impl(Ite1 i1, Ite2, i2){ std::swap(*i1, *i2); }};
上述计算tag就是判断,当两个迭代器所指类型相同,且所指对象的引用为真的引用时,可以不用建立临时对象,进行高效交换操作即可。
Type Traits
上述实现的对迭代器的所指类型的特性萃取,以及所指类型是否一样、是否是真的引用类型等实现,就是type traits。这是一种通过C++的模版进行模版实参推演在编译阶段实现的,巧妙利用全特化、偏特化,本质上就是使用元编程实现了条件语句,如果是上述的tag或者is_same
这种两种结果的就是if-else语句,如果条件多于两个,如iterator_traits
针对指针、引用、常量引用等多种特化版本,那么就是switch语句的实现相对应。据此,就可以很容易的理解各种type_traits
的原理并进行运用了。
- 模版元编程之——Type Traits
- 模版元编程:C++11中type traits的部分实现
- STL源码剖析——type traits编程技法
- STL的榨汁机——type traits
- C++—Traits编程技法
- STL之四 type traits
- type traits 之"本质论"
- c++模版元编程
- c++模版元编程
- 【C++模版之旅】神奇的Traits
- 【C++模版之旅】神奇的Traits
- 【C++模版之旅】神奇的Traits
- 【C++模版之旅】神奇的Traits
- 【C++模版之旅】神奇的Traits
- C++模板元编程——Traits
- 【C++】traits技术与模板元编程
- Type Traits
- type traits
- JDK中的URLConnection参数详解
- error C2065: 'Public_Area' : undeclared identifier
- 第5周项目4—长方柱类
- jQuery选择器案例
- java.lang.IllegalStateException: ApplicationEventMulticaster not initialized - call 'refresh' before
- 模版元编程之——Type Traits
- Qt提取EXE文件的大图标
- 笔试and心态
- 接口List<E>常用实现类分析
- Android开发随手记1
- AS项目新建错误:cant resolve symbol R
- C++模板类函数的使用以及如何使用时间作为文件名保存文件
- 第一次尝试博客
- QT7 How to connect Qt to SQLite