型別萃取(TypeTraits)
為什麼需要型別萃取
前面我們提到了迭代器,它是一個行為類似於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,還定義些其他的型別,具體的見前面部落格中所提到的迭代器的內嵌型別,迭代器:http://blog.csdn.net/bit_clearoff/article/details/53726462
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函式,也提升了程式碼的效能。
原文:https://blog.csdn.net/bit_clearoff/article/details/53728516