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

前面我们提到了迭代器,它是一个行为类似于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()); */
}

  

如果只有内嵌型别,会发生下面的情况: 
C++之萃取技术(traits)-LMLPHP

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

原生指针本来应该是迭代器当中的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函数了。 
C++之萃取技术(traits)-LMLPHP

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

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

05-11 18:26