为什么需要类型萃取(特化)

前面我们提到了迭代器,它是一个行为类似于smart pointer之类的东西,主要用于对STL容器中的对象进行访问,而且不暴露容器中的内部结构,而迭代器所指对象的型别称为该迭代器的value type;如果在实际的工程当中我们应该怎么获取STL容器中对象的value type 呢,这里面就需要用到C++中模板的特化了,我们先来看看下面的代码:

template <class T>
void Func()
{
cout << "非内置类型" << endl;
} template <>
void Func<int>()
{
cout << "内置的int类型" << endl;
}

  

我们可以通过特化来推导参数,如果我们自定义了多个类型,除非我们把这些自定义类型的特化版本写出来,否则我们只能判断他们是内置类型,并不能判断他们具体属于是个类型。

这时候,我们引入了内嵌型别,在类中将迭代器所指向的类型定义成value type,还定义些其他的型别,具体的见前面博客中所提到的迭代器的内嵌型别,迭代器:https://www.cnblogs.com/cthon/p/9206262.html

  typedef bidirectional_iterator_tag    iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;

    

但是这种情况还是有缺口的,例如下面的代码:

template <class T>
struct MyIter
{
typedef T ValueType;
T* _ptr;
MyIter(T* p = 0)
:_ptr(p)
{} T& operator*()
{
return *_ptr;
}
}; template<class Iterator>
typename Iterator::ValueType
Func(Iterator it)
{
return *it;
}

  

这里虽然看上去没有什么问题,但并不是所有的迭代器都有内嵌型别的,例如原生指针,因为原生指针不是类型别,所以我们无法为原生指针定义内嵌型别。

我们还可以看看下面这个例子:

这里先给出代码:

//Vector的代码
template <class T, class Alloc = alloc>
class Vector
{
public:
//vector的内嵌型别
typedef T ValueType;
typedef ValueType* Pointer;
typedef const ValueType* ConstPointer;
typedef ValueType* Iterator;
typedef const ValueType* ConstIterator;
typedef const ValueType* ConstPointer;
typedef ValueType& Reference;
typedef const ValueType& ConstReference;
typedef size_t SizeType;
typedef size_t DifferenceType;
public:
Vector()
: _start(0)
, _finish(0)
, _EndOfStorage(0)
{} //Vector() Iterator Begin()
{
return _start;
} Iterator End()
{
return _finish;
} ConstIterator cBegin() const
{
return _start;
} ConstIterator cEnd() const
{
return _finish;
} ValueType& operator[](const SizeType& n)
{
return *(_start + n);
} //Const ValueType& operator[](const SizeType& n) const
//{
// return *(_start + n);
//} void PushBack(const ValueType& x)
{
//扩容
_CheckCapacity(); Insert(_finish, x);
} void Insert(Iterator pos,const ValueType& x)
{
//SizeType n = pos - _begin;
//assert(pos>=_start&&pos<=_finish);
if (pos == _finish)
{
Construct(_finish, x);
_finish++;
}
else
{
//计算插入点之后的现有元素的个数
size_t ElemFront = _finish - pos; /* _finish++;*/
for (int i = 0; i < ElemFront; i++)
{
Iterator CopyPos = _finish - i;
Construct(CopyPos, *(CopyPos - 1));
}
_finish++;
Construct(pos, x);
}
} protected:
typedef SimpleAlloc<ValueType, Alloc> DataAllocator;
Iterator _start;
Iterator _finish;
Iterator _EndOfStorage;
protected:
void _CheckCapacity()
{
if (_finish == _EndOfStorage)
{
//空间已满,开辟空间
size_t OldSize = _finish - _start;
size_t NewSize = OldSize * 2 + 3;
T* _tmp = DataAllocator::allocate(NewSize); //开辟新的空间
for (size_t i = 0; i < OldSize; i++)
{
//拷贝原数组当中的成员
_tmp[i] = _start[i];
}
//改变参数
swap(_start, _tmp);
_finish = _start + OldSize;
_EndOfStorage = _start + NewSize; //释放空间
DataAllocator::deallocate(_tmp, OldSize);
}
}
}; void TestVector()
{
Vector<int> v1; v1.PushBack(1);
v1.PushBack(2);
v1.PushBack(3);
v1.PushBack(4);
v1.PushBack(5);
v1.PushBack(6); Vector<int>::Iterator it =v1.Begin(); while (it != v1.End())
{
cout << *it << " ";
it++;
} cout << "Distance?" << Distance(v1.Begin(), v1.End()) << endl;
} //求两个迭代器之间距离的函数 //普通类型的求两个迭代器之间距离的函数
template <class InputIterator>
inline size_t __Distance(InputIterator first, InputIterator last, InputIteratorTag) {
size_t n = 0;
while (first != last) {
++first; ++n;
}
return n;
} //RandomAccessIteratorTag类型的求两个迭代器之间距离的函数
template <class RandomAccessIterator>
inline size_t __Distance(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIteratorTag)
{
size_t n = 0;
n += last - first;
return n;
} //求两个迭代器之间的距离的函数
template <class InputIterator>
inline size_t Distance(InputIterator first, InputIterator last)
{
return __Distance(first, last,InputIterator::IteratorCategory());
/*return __Distance(first, last,IteratorTraits<InputIterator>::IteratorCategory()); */
}

  

如果只有内嵌型别,会发生下面的情况: 

这里图中已经给出了详细的解释。

原生指针本来应该是迭代器当中的RandomAccessIterator,这里因为无法为原生指针定义类型,所以我们就没有办法拿到原生指针的迭代器,这时候,类型萃取就出来了:

//类型萃取
template <class Iterator>
struct IteratorTraits
{
typedef typename Iterator::IteratorCategory IteratorCategory;
typedef typename Iterator::ValueType ValueType;
typedef typename Iterator::DifferenceType DifferenceType;
typedef typename Iterator::Pointer Pointer;
typedef typename Iterator::Reference Reference;
}; //原生指针的类型萃取
template <class T>
struct IteratorTraits<T*>
{
typedef RandomAccessIteratorTag IteratorCategory;
typedef T ValueType;
typedef ptrdiff_t DifferenceType;
typedef T* Pointer;
typedef T& Reference;
}; //针对const T*的类型萃取
template <class T>
struct IteratorTraits<const T*>
{
typedef RandomAccessIteratorTag IteratorCategory;
typedef T ValueType;
typedef ptrdiff_t DifferenceType;
typedef T* Pointer;
typedef T& Reference;
};

   

这时候,我们就能调用正确的__Distance函数了。 

类型萃取的设计模式提高了代码的复用性,在上面的例子中通过萃取出不同的迭代器的类型来调用不同迭代器的__Distance函数,也提升了代码的性能。

参考:http://www.cnitblog.com/weitom1982/archive/2008/12/19/7889.html

C++之萃取技术(traits)的更多相关文章

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

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

  2. C++11中的技术剖析(萃取技术)

    从C++98开始萃取在泛型编程中用的特别多,最经典的莫过于STL.STL中的拷贝首先通过萃取技术识别是否是已知并且支持memcpy类型,如果是则直接通过内存拷贝提高效率,否则就通过类的重载=运算符,相 ...

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

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

  4. 类型萃取(type traits)

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

  5. CGI 萃取技术 __type_traits

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

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

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

  7. STL--迭代器设计原则和萃取机制(Traits)

    title: C++ STL迭代器设计原则和萃取机制(Traits) date: 2019-12-23 15:21:47 tags: STL C/C++ categories: STL 迭代器 (it ...

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

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

  9. STL 萃取(Traits)机制剖析

    模板特化 在将萃取机制之前,先要说明模板特化 当有两个模板类,一个是通用泛型模板,一个是特殊类型模板,如果创建一个特殊类型的对象,会优先调用特殊的类型模板类,例如: template <type ...

随机推荐

  1. HDU 4403 A very hard Aoshu problem (DFS暴力)

    题意:给你一个数字字符串.问在字符串中间加'='.'+'使得'='左右两边相等. 1212  : 1+2=1+2,   12=12. 12345666 : 12+3+45+6=66.  1+2+3+4 ...

  2. 合并SO为单独交货单

    本场景为单步交货     为客户建立专用的route.     增加一个pull rule         在做订单的时候,为订单行选择 上面建立好的route,     连续建立了 2个 订单 SO ...

  3. 《图论》——广度优先遍历算法(BFS)

    十大算法之广度优先遍历: 本文以实例形式讲述了基于Java的图的广度优先遍历算法实现方法,详细方法例如以下: 用邻接矩阵存储图方法: 1.确定图的顶点个数和边的个数 2.输入顶点信息存储在一维数组ve ...

  4. php实现类似淘宝最近浏览商品的功能模型代码

    <?php //TempNum 显示临时记录数 $TempNum=5; //setcookie("RecentlyGoods", "12,31,90,39" ...

  5. [Maven实战](9)传递性依赖

    了解Spring的朋友都知道.创建一个Spring Framework项目都须要依赖什么样的Jar包.假设不使用Maven,那么在项目中就须要手动下载相关的依赖.因为Spring Framework又 ...

  6. Android------Intent.createChooser

    Intent的匹配过程中有三个步骤,包含Action , category与data 的匹配. 假设匹配出了多个结果.系统会显示一个dialog让用户来选    择.例如以下图: 那么今天我们主要是解 ...

  7. C/C++,java开源数学计算库

    有限元分析.数值计算.三维建模.信号处理.性能分析.仿真分析...这些或多或少与我们常用的软件息息相关,假如有一天你只需要这些大型软件系统的某一个很有限的功能,你是不是也要因此再用一用那动辄几个g的软 ...

  8. sublime 实用快捷键

    Command+Enter 在下一行插入新行.举个栗子:即使光标不在行尾,也能快速向下插入一行. Command+Shift+Enter 在上一行插入新行.举个栗子:即使光标不在行首,也能快速向上插入 ...

  9. Navicat Premium创建MySQL存储过程

    1.使用Navicat Premium打开创建函数向导,操作:连接名——数据库——函数——新建函数 2.选择过程——输入存储过程参数——完成(这一步可以不填写参数,编写存储过程代码的时候设置参数) 3 ...

  10. RS-485接口的防护电路设计

    RS-485总线标准是安防系统设备上应用最为广泛的物理层协议之一.RS-485的主要特点:支持远距离传输,长达4000英尺:双向信号差分传输,提高信号的噪音抑制能力,并且允许一条总线上可以挂接多个发射 ...