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

前面我们提到了迭代器,它是一个行为类似于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. C# 使用xenocode混淆加密【转】

    http://www.cnblogs.com/chendaoyin/archive/2013/05/03/3056692.html   之前就了解过混淆加密工具,但这还是第一次使用,选择了xenoco ...

  2. 非常easy的JAVA反射教程

    原创文章,转载请注明. 反射能够动态载入类,实例化对象,调用方法.如今以下面样例解说. 一.载入类. Class clazz = Class.forName("java.lang.Strin ...

  3. Effective C++ 条款一 视C++为一个语音联邦

    1.C语言         区块.语句.预处理器.内置数据类型.数组.指针等内容 2.OC++       类.封装.继承.多态.virtual函数 等 3.Template C++       泛型 ...

  4. jquery的一点点认识

    概述 JQuery是继prototype之后又一个优秀的Javascript库.它是轻量级的js库 .它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF 1.5+, Safari 2.0+, O ...

  5. Webkit JNI

    WebCoreFrameBridge.cpp BrowserFrame通过jni传下来的调用都会调用到WebCoreFrameBridge.cpp中的对应函数中,其他webkit的模块想回调信息给Br ...

  6. repeter中应用三元运算符

    应用情景一:根据ID显示名称例如:0代表启动,1:代表关闭例子如下 <td><%#Eval("ID").ToString() == "0" ? ...

  7. sql server t-sql脚本转成oracle plsql

    将一份SQL SERVER数据库生成的T-SQL脚本,转成ORACLE的PL/SQL,其复杂繁琐程度,远远出乎我的意料. 这份SQL SERVER脚本,里面有表,有视图,还有存储过程,以及一些自定义函 ...

  8. CSDN第一期总结之三:Thread的问题(转)

    C#是一门支持多线程的语言,因此线程的使用也是比较常见的.由于线程的知识在Win32编程的时候已经说得过多,所以在.Net中很少介绍这部分(可能.Net不觉得这部分是它所特有的). 那么线程相关的问题 ...

  9. iOS不用上架就能下载安装ipa应用内测:使用FIR.im发布自己的移动端APP

    本文转自:http://www.cnblogs.com/imzzk/p/firim.html 一次很偶然的机会知道fir.im,这家公司主要的产品就是帮助开发者方便便捷地发布iOS或者Android应 ...

  10. 各种数据库(oracle、mysql、sqlserver等)在Spring中数据源的配置和JDBC驱动包----转

    在开发基于数据库的应用系统时,需要在项目中进行数据源的配置来为数据 库的操作取得数据库连接.配置不同数据库的数据源的方法大体上都是相同的,不同的只是不同数据库的JDBC驱动类和连接URL以及相应的数据 ...