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

前面我们提到了迭代器,它是一个行为类似于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. nload 命令

    网卡 流量监控命令 // 安装 yum intall nload nload 上下page 键 切换网卡查看

  2. HEVC的參考队列解码

    參考队列是指在进行帧间解码时.P或者B slice所參考的已解码的.位于解码图像缓存中(DPB, decoded picture buffer)中的图像队列,类似h264中的reflist0和refl ...

  3. ionic 修改应用名称 、启动页出现黑白屏 及 修改百度离线地图 点聚合 图标

    1.ionic 修改应用名称 2.启动页打开后会在图片消失会出现一小段黑屏的时间 解决方法: 首先,启动页的图片消失时间默认是在config.xml配置的 <preference name=&q ...

  4. vue2.0 + vux (六)NewsList 资讯页 及 NewsDetail 资讯详情页

    设置代理,避免出现跨域问题 /*设置代理,避免出现跨域问题*/ proxyTable: { '/api':{ target: 'https://www.oschina.net/action/apiv2 ...

  5. meta标签多种用法

    <meta name=”google” content=”notranslate” /> <!-- 有时,Google在结果页面会提供一个翻译链接,但有时候你不希望出现这个链接,你可 ...

  6. python(21)- python内置函数练习

    题目一:用map来处理字符串列表啊,把列表中所有人都变成sb,比方alex_sbname=['alex','wupeiqi','yuanhao'] name=['alex','wupeiqi','yu ...

  7. 移植alsa-lib遇到的问题

    移植alsa-lib遇到的问题 linux audio    alsa lib    VERSIONED_SYMBOLS 这两天移植alsa lib时遇到了一个问题,被困住了好久. 做个记录,以后再被 ...

  8. unix改变shell显示颜色

    编写shell脚本的时候.通过改变shell的显示颜色,不但可以改变使用shell终端的体验,并且更为有用的是,可以通过改变显示内容的颜色来区分正常输出.warning和error等不同关注级别的输出 ...

  9. Error (167005): Can't assign I/O pad "GX_TX" to PIN_AG27 because this causes failure in the placement of the other atoms in its associated channel

    1.同时在两个GX的bank,建立两GX ip core 会出现 两个IP的cal_blk_clk信号,要保持是同一个时钟

  10. 每天一点儿Java--list

    import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; /** * ...