熬夜整理,万字讲解STL核心概念——iterator迭代器、Traits编程技术/偏特化、__type_traits编程技术

一、迭代器概述

  • 不论是泛型思想或是STL的实际运用,迭代器都扮演着重要的角色
  • STL的中心思想在于:将数据容器和算法分开,彼此独立设计,最后再以一种胶着剂将它们撮合在一起

二、迭代器的独立于容器和算法而存在的

  • 下面介绍一个容器、算法、迭代器在一起使用的案例
  • 演示案例如下:下面是find()算法的源码(摘录于<stl_algo.h>)
template<class InputIterator,class T>
InputIterator find(InputIterator first, InputIterator last,
    const T& value)
{
    while (first != last&&*first != value)
        ++first;
    return first;
}
  • 在程序中定义了不同的迭代器,find()能够根据不同的容器进行查找操作:
#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
#include <deque>

using namespace std;


int main()
{
    const int arraySize = 7;
    int ia[arraySize] = { 0,1,2,3,4,5,6 };

    vector<int> ivect(ia, ia + arraySize);
    list<int> ilist(ia, ia + arraySize);
    deque<int> ideque(ia, ia + arraySize);

    vector<int>::iterator iter1 = find(ivect.begin(), ivect.end(), 4);
    if (iter1 == ivect.end())
        std::cout << "4 not found" << std::endl;
    else
        std::cout << "4 found" << std::endl;

    list<int>::iterator iter2 = find(ilist.begin(), ilist.end(), 6);
    if (iter2 == ilist.end())
        std::cout << "6 not found" << std::endl;
    else
        std::cout << "6 found" << std::endl;

    deque<int>::iterator iter3 = find(ideque.begin(), ideque.end(), 8);
    if (iter3 == ideque.end())
        std::cout << "8 not found" << std::endl;
    else
        std::cout << "8 found" << std::endl;
    return 0;
}

  • 可以看出,迭代器是独立于容器与算法的

三、迭代器是一种smart pointer(智能指针)

  • 迭代器是一种行为类似指针的对象,而指针的各种行为中最常见的便是“内容提领”与“成员访问”。因此迭代器最重要的工作就是对operator*和operator->进行重载
  • 下面以auto_ptr为例:
    • auto_ptr现在已过时,取代物是unique_ptr。但是此处我们以auto_ptr来演示说明
    • auto_ptr是一个用来包装原生指针的对象
    • 下面是auto_ptr的简略化的源码:
template <class T>
class auto_ptr
{
public:
    explicit auto_ptr(T* p = 0) :pointee(p) {}
    template<class U>
    auto_ptr(auto_ptr<U>& rhs) : pointee(rhs.release()) {}
    ~auto_ptr() { delete pointee; }

    template<class U>
    auto_ptr<T>& operator=(auto_ptr<U>& rhs) {
        if (this != rhs)
            reset(rhs.release());
        return *this;
    }

    T& operator*()const { return *pointee; }
    T* operator->()const { return pointee; }
    T* get()const { return pointee; }
private:
    T* pointee;
};
  • 下面是auto_ptr的使用:
int main()
{
    auto_ptr<string> ps(new string("dongshao"));
	
    std::cout << *ps << std::endl;       //输出dongshao
    std::cout << ps->size() << std::endl;//输出5
	
    return 0;
}
  • 下面设计自己的迭代器:我们有一个自己的链表容器类List,其中ListItem是链表的节点类
template<typename T>
class List
{
public:
    void insert_front(T value);
    void insert_end(T value);
    void display(std::ostream &os = std::cout)const;

    ListItem* end()const { return _end; }
    ListItem* front()const { return _front; }
private:
    ListItem<T>* _end;
    ListItem<T>* _front;
    long _size;
};

template<typename T>
class ListItem
{
public:
    T value()const { reutrn value; }
    ListItem* next()const { return return _next; }
private:
    T _value;
    ListItem* _next;
};
  • 如何将我们的List应用于我们的find()函数呢?我们需要为它设计一个行为类似指针的外衣,也就是一个迭代器:
    • 我们提领(*)这个迭代器时,返回的是节点对象ListItem
    • 当我们递增该迭代器时,指向于下一个ListItem对象
  • 但是为了让迭代器适用于任何类型的节点,而不仅限于ListItem,我们将迭代器设计为一个类模板
template<class Item>
struct ListIter
{
    Item* ptr;

    ListIter(Item* p = 0) :ptr(p) {}

    Item& operator*()const { return *ptr; }
    Item* operator->()const { return ptr; }

    ListIter& operator++() { 
        ptr = ptr->next();
        return *this;
    }
    ListIter& operator++(int) {
        ListIter tmp = *this;
        ++*this;
        return tmp;
    }

    bool operator==(const ListIter& i)const { return ptr == i.ptr; }
    bool operator!=(const ListIter& i)const { return ptr != i.ptr; }
};
  • 由于find()算法内以*iter!=value来检查元素值是否吻合,而本例中的value的类型是int,iter的类型是ListItem<int>,两者不能直接进行比较,因此还需要设计一个全局的operator!=重载函数,并以int和ListItem<int>为参数:
template<typename T>
bool operator!=(const ListItem<T>& item, T n)
{
    return (item->value() != n);
}
  • 现在我们可以使用我们自己的容器与迭代器了:
int main()
{
    List<int> myList;

    for (int i = 0; i < 5; ++i) {
        myList.insert_front(i);
        myList.insert_end(i + 2);
    }

    ListIter<ListItem<int> > begin(myList.front());
    ListIter<ListItem<int> > end;
    ListIter<ListItem<int> > iter;

    iter = find(begin, end, 3);
    if (iter == end)
        std::cout << "3 not found" << std::endl;
    else 
        std::cout << "3 found" << std::endl;

    return 0;
}
  • 从上面的实现可以看出,为了完成一个针对List而设计的迭代器,我们暴露了太多List实现细节:
    • 在main函数中,为了制作begin()和end()两个迭代器,我们暴露了ListItem
    • 在ListIter类中为了达成operator++的目的,我们暴露了ListItem的操作函数next()
  • 如果不是为了迭代器的设计,ListItem原本应该完全隐藏。所以,为了避免暴露我们设计容器的细节,我们应该将迭代器的所有实现细节也封装其容器类中,这也正是每一个STL容器都提供了一份专属于自己的迭代器的缘故。

四、迭代器相应类型

  • 前文介绍了迭代器的雏形,在使用迭代器的时候,我们可能需要知道这个迭代器所指之物的数据类型,但是C++不支持typeof()这种机制,因此我们需要一种技术来获取迭代器所指之物的类型
  • 参数推导机制:参数推导机制可以让我们推断出参数的数据类型
template<class I,class T> //此例中,T为int
void func_impl(I iter, T t)
{
    T tmp;
    //...
}

template<class I>
inline void func(I iter)
{
    func_impl(iter, *iter);
}

int main()
{
    int i;
    func(&i);
}

  • 迭代器相应类型不只是“迭代器所指对象的类型”一种而已。最常见的相应类型有5种,在后面介绍

五、声明内嵌类型

  • 上面介绍了函数的参数推导机制,推导机制虽然好,但是只能获取参数的类型,不能获取函数返回值的类型
  • 作为解决办法,声明内嵌类型是一个好主意,例如:
  • 声明内嵌类型:下面我们在MyIter中使用typedef声明一个别名,这个别名就是传入迭代器的类型的别名。在func函数中,我们返回迭代器中的元素,因此我们需要知道迭代器所指之物的类型才能为其声明返回值类型,此处我们使用了typename来引出迭代器中的类型别名
//迭代器类
template<class T>
struct MyIter {
    typedef T value_type; //内嵌类型别名,用value_type来表示迭代器类所指之物的类型
    T* ptr;
    MyIter(T* p = 0) :ptr(p) {}
    T& operator*()const { return *ptr; }
};

/*
    为什么要使用typename:因为编译器不知道T是什么,也就是说编译器不知道MyIter<T>::value_type
    是一个类型还是一个成员函数还是一个成员变量,此处我们使用typename告诉编译器这是一种类型,
    这样才能顺利编译通过
*/
template<class I>
typename I::value_type  //一整行为func的返回值类型
func(I ite){
    return *ite;
}

MyIter<int> ite(new int(8));
cout << func(ite);

六、偏特化

  • 上面的声明内嵌类型很好用,但是并不是所有迭代器的都是class,例如原生指针就不是class(例如int*,stirng*),那么此时我们就无法使用内嵌类型了,但是STL算法、容器等都是接受原生指针作为一种迭代器的。那么针对于原生指针我们该如何处理呢?
  • 是的,偏特化(Paritial Specialization)可以做到。
  • 偏特化的概念是:我们可以针对类模板中的template参数进行特化工作。为其提供一个特化版本(也就是讲template中参数赋予明确的指定)
  • 例如下面是常规的类模板,允许接收T为任何类型
template<typename T>
class C{};
  • 下面是一个特化版本,允许接收T为原生指针的情况 
template<typename T>
class C<T*>{};
  • 为迭代器声明一个偏特化:在“二”中我们的迭代器没有偏特化版本,因此传入原生指针之后就不可以推导出迭代器所指之物的类型了。有了偏特化,我们就可以对迭代器进行偏特化,设计出下面的迭代器
//偏特化迭代器类
template<class T>
struct MyIter<T*> {
    typedef T value_type; //这个迭代器接受原生指针,从而获得指针所指之物的数据类型
    T* ptr;
    MyIter(T* p = 0) :ptr(p) {}
    T& operator*()const { return *ptr; }
};

七、Traits编程技术

  • 通过上面的介绍,我们正式开始介绍Traits编程技术
  • 下面的类模板专门用来“萃取”迭代器的类型,value_type就是迭代器的特性之一:
//如果I为迭代器类,并且迭代器类中声明有类型别名,那么我们就可以萃取出其中的数据类型

template<class I>
struct iterator_traits {
    typedef typename I::value_type value_type;
};
  • traits是指:如果I定义有自己的value type,那么我们就可以通过上面设计的iterator_traits类来萃取出I的value_type(数据类型)。我们上面的func函数还可以更改为:
//萃取的使用场景
template<class I>
typename iterator_traits<I>::value_type  //一整行为func的返回值类型
func(I ite){
    return *ite;
}
  • traits也可以有特化版本:例如下面只针对于迭代器是原生指针的偏特化版本,其能萃取出指针所指之物的数据类型
template<class T>
struct iterator_traits<T*> {
    typedef T value_type;
};
  • 注意“指向常数对象的指针”特化本:针对“指向常数对象的指针”,下面的式子会得出什么结果呢?
iterator_traits<const int*>::value_type;
  • 我们希望这种traits机制来获取迭代器的类型,但是如果上面的式子萃取出的是const int类型,那么声明就无法赋值了,因此如果迭代器是个pointer-to-const,我们应该设置下面的特化版本
template<class T>
struct iterator_traits<const T*> {
    typedef T value_type;  //萃取出的是T,而不是const T
};
  • 下图说明了traits所扮演的“特性萃取机”角色,萃取各个迭代器的特性
  • 当然如果希望“特性萃取机”traits能够正常工作,前提是迭代器必须在自己的类中声明类型别名(typedef)

八、iterator_traits萃取机

  • 下面是迭代器源码中的iterator_traits类(其中的一个版本),其用来萃取迭代器中的相应类型
//迭代器的源码
template<class Category,class T,class Distance=ptrdiff_t,
	class Pointer=T*,class Reference=T&>
struct iterator {
	typedef Category iterator_category;
	typedef T value_type; //迭代器所指之物的数据类型(其余的见下面介绍)
	typedef Distance difference_type;
	typedef Pointer pointer;
	typedef Reference reference;
};


//traits萃取机,其中I为迭代器类
template<class I>
struct iterator_traits {
    typedef typename I::iterator_category iterator_category;
    typedef typename I::value_type value_type;
    typedef typename I::difference_type difference_type;
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};
  • 当然,iteraotr_traits类还必须设计针对于迭代器是pointer以及pointer-to-const的特化版本(见后面一篇文章得迭代器介绍源码)
  • 常用的迭代器相应类型有5中:value type、difference type、pointer、reference、iterator catagory,在下面一一介绍

九、迭代器相应类型之:value type

  • 所谓value type,就是指迭代器所指对象的类型
  • 任何一个与STL搭配的迭代器,都应该定义自己的value type内嵌类型

十、迭代器相应类型之:difference type

  • difference type用来表示两个迭代器之间的距离
  • 因此可以用来表示一个容器的最大容量(对于连续空间的容器而言,头尾之间的距离就是其最大容量)
  • 如果一个泛型算法提供计数功能,例如STL的count()算法,其返回值就必须使用迭代器的difference type
template<class I,class T>
typename iterator_traits<I>::difference_type //函数的返回值类型
count(I first, I last, const T& value) {
	typename iterator_traits<I>::difference n = 0;
	for (; first != last; ++first)
		++n;
	return n;
}
  • 针对于difference type的特化版本:针对相应类型difference type,traits设计了(针对原生指针)下面两个特化版本,以C++内建的ptrdiff_t作为原生指针的difference type
//针对于原生指针的偏特化版本
template<class I>
struct iterator_traits<T*> {
    //..
    typedef ptrdiff_t difference_type;
    //...
};

//针对于pointer-to-const的偏特化版本
template<class I>
struct iterator_traits<const T*> {
    //..
    typedef ptrdiff_t difference_type;
    //...
};

十一、迭代器相应类型之:reference type

  • reference  type传回一个运用,指向于迭代器所指之物
  • 从“迭代器所指之物内容是否允许改变”角度看,迭代器分为两种:
    • 不允许改变“所指对象的内容”的,称为constant iterators。例如const int* pic;
    • 允许改变“所指对象的内容”的,称为mutable iterators。例如int* pi;
  • 当我们对一个mutable iterators进行提领(*)操作时,获得的不应该是一个右值,而应该是一个左值,因为右值不允许赋值操作,左值才可以
  • 例如:

  • 如果想要传回左值,都是以by reference的方式进行(引用),所以如果迭代器是mutable iterators类型的,那么其value type是T,*p返回的不应该是T,而应该是T&(引用)
  • 源码如下:
template<class I>
struct iterator_traits {
	typedef typename I::reference reference;
};

//针对于原生指针设计的偏特化版本
template<class I>
struct iterator_traits<T*> {
	typedef T& reference;
};

//针对于pointer-to-const设计的偏特化版本
template<class I>
struct iterator_traits<const T*> {
	typedef const T& reference;
};

十二、迭代器相应类型之:pointer type

  • pointer type传回一个指针,指向于迭代器所指之物。源码如下:
template<class I>
struct iterator_traits {
	typedef typename I::pointer pointer;
};

//针对于原生指针设计的偏特化版本
template<class I>
struct iterator_traits<T*> {
	typedef T* pointer;
};

//针对于pointer-to-const设计的偏特化版本
template<class I>
struct iterator_traits<const T*> {
	typedef const T* pointer;
};

十三、迭代器相应类型之:iterator_category

  • 该迭代器类型用在规模较大的工程中
  • 先介绍迭代器的种类:根据移动特性与施行操作,迭代器被分为五类:
    • Input Iterator:这种迭代器所指的对象,不允许外界改变。只读
    • Output Iterator:只写
    • Forward Iterator:允许“写入型”算法(如replace())在此迭代器所形成的区间上进行读写操作
    • Bidirectional Iterator:可双向移动。某些算法需要逆向遍历某个迭代器区间(例如逆向拷贝某范围内的元素),可以使用这种类型的迭代器
    • Random Access Iterator:这种迭代器涵盖了上面4中迭代器所有的功能
  • 根据迭代器的分类,可以用下图来表示迭代器的从属关系(其中下层的拥有上层的能力):

  • 设计算法时,如果可能尽量针对图中的某种迭代器提供一个明确定义,并针对更强化的某种迭代器提供另一种定义,这样才能在不同的情况下提供最大效率:假设有一个算法可接受Forward Iterator,如果你以一个Random Access Iterator换递给它当然可以,但是Random Access Iterator不是最佳的选择
  • 下面是五个类,用来表示五种迭代器的类型,类中不需要任何成员,至于为什么使用继承机制(下面介绍):
//用于标记迭代器的类型
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag :public input_iterator_tag {};
struct bidirectional_iterator_tag :public forward_iterator_tag {};
struct random_access_iterator_tag :public bidirectional_iterator_tag {};
  • 将迭代器类进行继承的好处是:我们可以不必再写“单纯只做传递调用”的函数(例如下面要介绍的advance() ForwardIterator八本)。考虑下满的例子,从其中的输出可以看到如下的端倪:

#include <iostream>

using namespace std;

struct B {};            //B模拟InputIterator
struct D1 :public B {}; //D1模拟ForwardIterator
struct D2 :public D1 {};//D2模拟BidirectionalIterator

template<class I>
void func(I& p, B)
{
    std::cout << "B version" << std::endl;
}

template<class I>
void func(I& p, D2)
{
    std::cout << "D2 version" << std::endl;
}

int main()
{
    int *p;
    func(p, B());  //参数匹配,输出“B version”
    func(p, D1()); //参数不匹配,因继承关系输出“B version”
    func(p, D2()); //参数匹配,输出“D2 version”

    return 0;
}

  • 以advance()函数为例,介绍什么是iterator_category:advance()函数是一个内部算法,该函数有两个参数:一个是迭代器p和数值n(用来将迭代器p进行n次前进)。下面是advance的三份定义:
    • 一份针对于Bidirectional Iterator
    • 一份针对于Random Access Iterator
    • 一份针对于Forward Iterator
template<class InputIterator,class Distance>
void advance_II(InputIterator& i, Distance n)
{
    //单向,逐一前进
    while (n--)
        ++i;
}

template<class BidirectionalIterator, class Distance>
void advance_BI(BidirectionalIterator& i, Distance n)
{
    //双向,逐一前进
    if (n >= 0)
        while (n--)
            ++i;
    else
        while (n++)
            --i;
}

template<class RandomAccessIterator, class Distance>
void advance_RAI(RandomAccessIterator& i, Distance n)
{
    //双向,跳跃前进
    i += n;
}
  • 我们有三份advance(),那么我们调用的时候调用哪种类型的advance呢?
  • 我们可以使用重载机制,在上面的advance()函数中添加第三个参数,用来表示该advance()函数所适用的迭代器类型,此时可以考虑使用trsits的萃取功能萃取出迭代器的类型
  • 现在重新定义__advance()函数,添加第三个参数代表迭代器的类型,如下所示:
template<class InputIterator,class Distance>
void __advance(InputIterator& i, Distance n,input_iterator_tag)
{
    //单向,逐一前进
    while (n--)
        ++i;
}

//这是一个单纯的传递调用函数,在上面迭代器继承中介绍如何去除它
template<class RandomAccessIterator, class Distance>
void __advance(RandomAccessIterator& i, Distance n,forword_iterator_tag)
{
    advance(i,n,input_iterator_tag());
}

template<class BidirectionalIterator, class Distance>
void __advance(BidirectionalIterator& i, Distance n,bidirectional_itertator_tag)
{
    //双向,逐一前进
    if (n >= 0)
        while (n--)
            ++i;
    else
        while (n++)
            --i;
}

template<class RandomAccessIterator, class Distance>
void __advance(RandomAccessIterator& i, Distance n,random_access_iterator_tag)
{
    //双向,跳跃前进
    i += n;
}
  • 现在创建一个上层接口该接口接受一个迭代器,然后通过萃取迭代器中的iterator_category()来获得迭代器类型,然后再根据迭代器的类型调用上面的不同__advance()函数
//备注:此函数的命名以InputIterator为名,因为InputIterator为最低阶迭代器类型
template<class InputIterator, class Distance>
void __advance(InputIterator& i, Distance n, input_iterator_tag)
{
    __advance(i, n, iterator_traits<InputIterator>::iterator_category());
}
  • 上述代码中的iterator_traits<InputIterator>::iterator_category()将产生一个临时对象,其类型是上面介绍的五种迭代器类型之一,然后根据这个类型,编译器决定调用哪一种__advance()函数
  • 关于上面的函数,SGI STL<stl_iterator.h>中的源码是:

  • 因此,为了满足上面的行为,traits必须再增加一个相应类型:
template<class I>
struct iterator_traits {
    typedef typename I::iterator_category interator_category;
};

//针对原生指针设计的偏特化版本
template<class T>
struct iterator_traits<T*> {
    //注意,原生指针是一种Random Access Iterator
    typedef random_access_iterator_tag interator_category;
};

//针对于pointer-to-const设计的偏特化版本
template<class T>
struct iterator_traits<const T*> {
    typedef random_access_iterator_tag interator_category;
};
  • 以distance()函数为例,介绍什么是iterator_category:上面以advance()函数介绍了iterator_category的运用场景,下面介绍在distance()函数中的应用场景。distance()函数用来计算两个迭代器之间的距离。根据迭代器种类的不同,计算的方式也不同。下面是两种__distance()的函数定义
template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIteratorlast last,
    input_iterator_tag)
{
    iterator_traits<InputIterator>::difference_type n = 0;
    //逐一累计距离
    while (first != last) {
        ++first; ++n;
    }
    return n;
}

template<class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,
    random_access_iterator_tag)
{
    //直接计算差距
    return last - first;
}
  • 下面定义一个顶层接口,根据传入迭代器,萃取其类型,然后调用不同种类的__distance()函数:
template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIteratorlast last,
	input_iterator_tag)
{
    //萃取出迭代器的类型
    typedef typename iterator_traits<InputIterator>::iterator_category category;
    return __distance(first, last, category()));
}
  •  注意事项:
    • distance()函数可接受任何类型的迭代器,在上面介绍了迭代器的继承,当传入的迭代器为Oupt Iterator或Forward Iterator或Bidirectional Iterator时,回去调用Input Iterator版本的__distance()函数
    • 而传入RandomAccessIterator时,调用的就是RandomAccessIterator版本的__distance()版本的函数

十四、traits技术得保证

  • 为了使迭代器支持traits技术,STL提供了一个iterator类,每个新设计的迭代器都要继承于这个iterato类iterator类中不含任何成员,纯粹都是类型定义,后面三个参数都有默认值,因此新的迭代器只需提供前两个参数即可:
template<class Category,class T,
    class Distance=ptrdiff_t,class Pointer=T*,class Reference=T&>
struct iterator {
    typedef Category iterator_category;
    typedef T value_type;
    typedef Distance difference_type;
    typedef Pointer pointer;
    typedef Reference reference;
};
  • 有了上面的类之后,我们的iterator_traits类就可以轻易的萃取出迭代器的类型了
  • 例如,下面我们将前一篇文章中我们自定义的迭代器ListIter进行改写,使其继承于iterator:
template<class Item>
struct ListIter :
    public std::iterator<std::forward_iterator_tag, Item>
{

};

十五、iterator源代码实例

  • 下面列出了SGI STL<stl_iterator.h>头文件的代码
  • 关于iostream iterators、inserter iterators、reverse iterators的实现,在后面配接器中介绍
  • 迭代器类型:
//用于标记迭代器的类型
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag :public input_iterator_tag {};
struct bidirectional_iterator_tag :public forward_iterator_tag {};
struct random_access_iterator_tag :public bidirectional_iterator_tag {};
  • iterator:为了防止代码出错,自己定义的迭代器最好都继承这个类
template<class Category,class T,
    class Distance=ptrdiff_t,class Pointer=T*,class Reference=T&>
struct iterator {
    typedef Category iterator_category;
    typedef T value_type;
    typedef Distance difference_type;
    typedef Pointer pointer;
    typedef Reference reference;
};
  • traits萃取机:
//萃取机traits
template<class Iterator>
struct iterator_traits {
    typedef typename Iterator::iterator_category iterator_category;
    typedef typename Iterator::value_type value_type;
    typedef typename Iterator::difference_type difference_type;
    typedef typename Iterator::pointer pointer;
    typedef typename Iterator::reference reference;
};

//针对于原生指针的偏特化版本
template<class Iterator>
struct iterator_traits<T*> {
    typedef random_access_iterator_tag iterator_category;
    typedef T value_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef T& reference;
};

//针对于pointer-to-const的偏特化版本
template<class Iterator>
struct iterator_traits<const T*> {
    typedef random_access_iterator_tag iterator_category;
    typedef T value_type;
    typedef ptrdiff_t difference_type;
    typedef const T* pointer;
    typedef const T& reference;
};
  • 下面这个函数可以方便的萃取出某个迭代器的类型(category)
//参数为迭代器
template<class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
    iterator_category(const Iterator&)
{
    typedef typename iterator_traits<Iterator>::iterator_category category;
    return category();
}
  •  下面这个函数可以方便的萃取出某个迭代器的distance type
//参数为迭代器
template<class Iterator>
inline typename iterator_traits<Iterator>::difference_type*
    distance_type(const Iterator&)
{
    return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}
  • 下面这个函数可以方便的萃取出某个迭代器的value type
//参数为迭代器
template<class Iterator>
inline typename iterator_traits<Iterator>::value_type*
    distance_type(const Iterator&)
{
    return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}
  • 整组distance函数:
template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
__distance(InputIterator first, InputIteratorlast last,
    input_iterator_tag)
{
    iterator_traits<InputIterator>::difference_type n = 0;
    while (first != last) {
        ++first; ++n;
    }
    return n;
}

template<class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,
    random_access_iterator_tag)
{
    return last - first;
}

template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIteratorlast last,
	input_iterator_tag)
{
	typedef typename iterator_traits<InputIterator>::iterator_category category;
	return __distance(first, last, category()));
}
  • 整组advance函数:
template<class InputIterator,class Distance>
void __advance(InputIterator& i, Distance n,input_iterator_tag)
{
    //单向,逐一前进
    while (n--)
        ++i;
}

template<class BidirectionalIterator, class Distance>
void __advance(BidirectionalIterator& i, Distance n,bidirectional_itertator_tag)
{
    //双向,逐一前进
    if (n >= 0)
        while (n--)
            ++i;
    else
        while (n++)
            --i;
}

template<class RandomAccessIterator, class Distance>
void __advance(RandomAccessIterator& i, Distance n,random_access_iterator_tag)
{
    //双向,跳跃前进
    i += n;
}

template<class InputIterator, class Distance>
void __advance(InputIterator& i, Distance n, input_iterator_tag)
{
    __advance(i, n, iterator_category(i));
}

十六、__type_traits概述

  • 前一篇文章中我们介绍了traits技术:https://blog.csdn.net/qq_41453285/article/details/103540156
  • STL只对迭代器加以规范,制定出iterator_traits这样的东西。SGI把这种技术进一步扩大到迭代器之外的地方,于是有了_type_traits
  • 双下划线意思指这是SGI STL内部使用的东西,不在STL标准之内

十七、__type_traits的使用目的是什么

  • traits技术用来萃取迭代器的类型
  • 而__type_traits用来萃取类型有什么特性,例如:
    • 这个类型是否具有non-trivial构造函数
    • 这个类型是否具有non-trivial拷贝构造函数
    • 这个类型是否具有non-trivial拷贝赋值运算符
    • 这个类型是否具有non-trivial析构函数
  • trivial与non-trivial:trivial意思是无意义,这个trivial和non-trivial是对类的四种函数来说的:
    • 构造函数(ctor)
    • 复制构造函数(copy)
    • 赋值函数(assignment)
    • 析构函数(dtor)
  • 如果至少满足下面3条里的一条:
    • 显式(explict)定义了这四种函数。
    • 类里有非静态非POD的数据成员。
    • 有基类
  • 那么上面的四种函数是non-trivial函数,比如叫non-trivial ctor、non-trivial copy…,也就是说有意义的函数,里面有一下必要的操作,比如类成员的初始化,释放内存等
  • 如果class内部含有指针成员,并且对它进行内存动态分配,那么这个class就需要实现自己的non-trivial-xxx
  • 当我们有了对象的数据类型之后,又萃取出这个数据类型是否具有上面的那些特性,那么我们就可以调用不同的处理方法来处理对象

十八、__typr_traits的使用方法

  • 例如我们可以使用下面的方法使用__type_traits,其中T代表任意的数据类型:
//下面的式子应该返回真或假,然后好让我们进一步判断
__type_traits<T>::has_trivial_default_constructor;
__type_traits<T>::has_trivial_copy_constructor;
__type_traits<T>::has_trivial_assignment_operator;
__type_traits<T>::has_trivial_destructor;
__type_traits<T>::is_POD_type;
  • 对于上面的式子,我们希望对传入的数据类型T,萃取出其是否具有non-trivial构造/拷贝构造等也就是上面的式子应该返回“真”或“假”
  • 但是我们不希望上面的代码返回bool值,而是返回类,于是STL中定义了下面的两个类,用来表示“真”或“假”:
//没有任何数据成员,单纯的用来表示“真”或“假”
struct __true_type {};  //真
struct __false_type {}; //假
  • __type_traits类的定义:__type_traits就是用来萃取对传入的数据类型是否具有trivial构造函数/拷贝构造/赋值运算符/析构函数/POD性质等
template<class type>
struct __type_traits {
    typedef __true_type this_dummy_must_be_first;
    /*
    不要移除这个成员,它通知“有能力自动将__type_traits特化”的编译器说,我们现在所看到的这个
    __type_traits_template是特殊的。这是确保万一编译器也使用一个名为__type_traits而其实
    与此处定义并无任何关联的template时,所有事情可以顺序编译通过
    */


    /*
    下面的成员可以自己添加/删除,用来表示某种数据类型是否具有某种特性
    */
    typedef __false_type has_trivial_default_constructor;
    typedef __false_type has_trivial_copy_constructor;
    typedef __false_type has_trivial_assignment_operator;
    typedef __false_type has_trivial_destructor;
    typedef __false_type is_POD_type;
};
  • 上面的成员都定义为__false_type。这是SGI最保守的值,然后在下面会看到针对每一个数据类型又设计了适当的__type_traits特化版本
  • 下面是<type_traits.h>中对所有C++类型定义的__type_traits的特化版本
/*
    下面是C++针对基本数据类型char、signed char、unsigned char、short、unsigned short、int
    、unsigned int、long、unsigned long、float、double、long提供的特化版本
    
    注意:每一个成员都是__true_type的,因此这些数据类型可以用最快速的方法(memcpy)来
    进行拷贝或赋值操作

    __STL_TEMPLATE_NULL是定义在<stl_config.h>中的,定义为template<>   
*/


template<class type>
__STL_TEMPLATE_NULL struct __type_traits<char> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<signed char> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<unsigned char> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};


template<class type>
__STL_TEMPLATE_NULL struct __type_traits<short> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<unsigned short> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<int> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<unsigned int> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<long> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<unsigned long> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<float> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<double> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

template<class type>
__STL_TEMPLATE_NULL struct __type_traits<long double> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

//针对原生指针设计的特化版本
template<class type>
__STL_TEMPLATE_NULL struct __type_traits<T*> {
	typedef __true_type this_dummy_must_be_first;

	typedef __true_type has_trivial_default_constructor;
	typedef __true_type has_trivial_copy_constructor;
	typedef __true_type has_trivial_assignment_operator;
	typedef __true_type has_trivial_destructor;
	typedef __true_type is_POD_type;
};

十九、__type_traits实际运用①

  • 例如uninitialized_fill_n()全局函数:
template <class ForwardIterator,class Size,class T>
inline ForwardIterator uninitialized_fill_n(ForwardIterator first, Size n, const T& x)
{
    return __uninitialized_fill_n(first, n, x, value_type(first));
}

//value_type()函数用来萃取first的数据类型,见前一篇文章
  • 该函数以x为蓝本,自迭代器first开始构造n个元素
  • 下面是__uninitialized_fill_n()函数的定义
template <class ForwardIterator, class Size, class T,class T1>
inline ForwardIterator __uninitialized_fill_n(ForwardIterator first,
	Size n, const T& x,T1*)
{
    //萃取数据类型T1是否具有POD特性
    typedef typename __type_traits<T1>::is_POD_type is_POD;
    //然后调用该函数
    return __uninitialized_fill_n_aux(first, n, x, is_POD());
}
  • 函数中萃取出数据类型中是否具有POD性质,于是会返回__false_type类或者__true_type类,然后将类传入 __uninitialized_fill_n_aux()函数的第三个参数中
  • __uninitialized_fill_n_aux()函数根据传入的类类型,然后再调用不同的版本
//不具有POD性质的函数
template <class ForwardIterator, class Size, class T>
inline ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first,
	Size n, const T& x, __false_type)
{
    ForwardIterator cur = first;
    //为了阅读方便,此处省略了异常处理
    for (; n > 0; --n, ++cur)
        construct(&*cur, x);
    return cur;
}

//具有POD性质的函数
//如果拷贝构造函数等同于拷贝赋值运算符,而且有trivial析构函数,那么下面就有效
template <class ForwardIterator, class Size, class T>
inline ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first,
    Size n, const T& x, __true_type)
{
    return fill_n(first, n, x);
}
//定义于<stl_algobase.h>中
template <class OutputIterator, class Size, class T>
OutputIterator fill_n(OutputIterator first,Size n, const T& val)
{
    for (; n > 0; --n, ++first)
        *first = value;
    return first;
}

二十、__type_traits实际运用②

  • 第二个例子就是负责对象析构的destory()全局函数,在前面文章有介绍

二十一、__type_traits实际运用③

  • copy()算法解析参阅:https://blog.csdn.net/qq_41453285/article/details/104285962
  • copy()算法有非常多的特化与强化版本,根据不同的数据类型调用不同的方法来进行拷贝
  • 下面是指简略版本,详细介绍请参阅上面的链接
  • 源码:下面是copy的底层实现,它会萃取参数是否具有trivial拷贝构造函数,然后调用不同的版本
template<class T>
inline void copy(T* source, T* destination, int n)
{
    copy(source, destination, n, 
        typename __type_traits<T>::has_trivial_copy_constructor());
}
  • 下面是copy()的底层实现
//拥有一个non-trivial拷贝构造函数,调用这一版本
template<class T>
void copy(T* source, T* destination, int n, __false_type)
{
    //...
}

//拥有一个trivial拷贝构造函数,调用这一版本
//其中直接使用memcpy()完成工作
template<class T>
void copy(T* source, T* destination, int n, __true_type)
{
    //...
}

二十二、对自己的类施加__type_traits机制

  • 下面是<stl_traits.h>中的声明:

  • 因此,你可以在自己的程序中充分利用这个__type_traits机制
  • 演示案例:例如你自己定义了一个Shape类,那么如果你将Shape类传入__type_traits,默认情况下,萃取出的特性都是__false_type的,因为在“三”中我们定义的__type_traits原型来说,你的Shape没有特化版本,默认的情况下全部返回“假”(也就是返回__false_type类)。因此,你可以自己设计一个__type_traits特化版本,针对于你自己的类型
template<class type>
__STL_TEMPLATE_NULL struct __type_traits<Shape> {
	typedef __true_type this_dummy_must_be_first;

    //下面的内容自己定义(为真还是为假)
	typedef __false_type has_trivial_default_constructor;
	typedef __false_type has_trivial_copy_constructor;
	typedef __false_type has_trivial_assignment_operator;
	typedef __false_type has_trivial_destructor;
	typedef __false_type is_POD_type;
};
  • 至于你自己的class是否具有trivial-xxx还是non-trivial-xxx,参阅上面的介绍。

  • 我是小董,V公众点击"笔记白嫖"解锁更多【C++ STL源码剖析】资料内容。

 

相关推荐
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页