热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

二、迭代器的独立于容器和算法而存在的
  • 下面介绍一个容器、算法、迭代器在一起使用的案例
  • 演示案例如下:下面是find()算法的源码(摘录于)

template
InputIterator find(InputIterator first, InputIterator last,const T& value)
{while (first != last&&*first != value)++first;return first;
}

  • 在程序中定义了不同的迭代器,find()能够根据不同的容器进行查找操作:

#include
#include
#include
#include
#include using namespace std;int main()
{const int arraySize &#61; 7;int ia[arraySize] &#61; { 0,1,2,3,4,5,6 };vector ivect(ia, ia &#43; arraySize);list ilist(ia, ia &#43; arraySize);deque ideque(ia, ia &#43; arraySize);vector::iterator iter1 &#61; find(ivect.begin(), ivect.end(), 4);if (iter1 &#61;&#61; ivect.end())std::cout <<"4 not found" <::iterator iter2 &#61; find(ilist.begin(), ilist.end(), 6);if (iter2 &#61;&#61; ilist.end())std::cout <<"6 not found" <::iterator iter3 &#61; find(ideque.begin(), ideque.end(), 8);if (iter3 &#61;&#61; ideque.end())std::cout <<"8 not found" <}

  • 可以看出&#xff0c;迭代器是独立于容器与算法的

三、迭代器是一种smart pointer&#xff08;智能指针&#xff09;
  • 迭代器是一种行为类似指针的对象&#xff0c;而指针的各种行为中最常见的便是“内容提领”与“成员访问”。因此迭代器最重要的工作就是对operator*和operator->进行重载
  • 下面以auto_ptr为例&#xff1a;
    • auto_ptr现在已过时&#xff0c;取代物是unique_ptr。但是此处我们以auto_ptr来演示说明
    • auto_ptr是一个用来包装原生指针的对象
    • 下面是auto_ptr的简略化的源码&#xff1a;

template
class auto_ptr
{
public:explicit auto_ptr(T* p &#61; 0) :pointee(p) {}templateauto_ptr(auto_ptr& rhs) : pointee(rhs.release()) {}~auto_ptr() { delete pointee; }templateauto_ptr& operator&#61;(auto_ptr& rhs) {if (this !&#61; 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的使用&#xff1a;

int main()
{auto_ptr ps(new string("dongshao"));std::cout <<*ps <size() <}

  • 下面设计自己的迭代器&#xff1a;我们有一个自己的链表容器类List&#xff0c;其中ListItem是链表的节点类

template
class List
{
public:void insert_front(T value);void insert_end(T value);void display(std::ostream &os &#61; std::cout)const;ListItem* end()const { return _end; }ListItem* front()const { return _front; }
private:ListItem* _end;ListItem* _front;long _size;
};template
class ListItem
{
public:T value()const { reutrn value; }ListItem* next()const { return return _next; }
private:T _value;ListItem* _next;
};

  • 如何将我们的List应用于我们的find()函数呢&#xff1f;我们需要为它设计一个行为类似指针的外衣&#xff0c;也就是一个迭代器&#xff1a;
    • 我们提领&#xff08;*&#xff09;这个迭代器时&#xff0c;返回的是节点对象ListItem
    • 当我们递增该迭代器时&#xff0c;指向于下一个ListItem对象
  • 但是为了让迭代器适用于任何类型的节点&#xff0c;而不仅限于ListItem&#xff0c;我们将迭代器设计为一个类模板

template
struct ListIter
{Item* ptr;ListIter(Item* p &#61; 0) :ptr(p) {}Item& operator*()const { return *ptr; }Item* operator->()const { return ptr; }ListIter& operator&#43;&#43;() { ptr &#61; ptr->next();return *this;}ListIter& operator&#43;&#43;(int) {ListIter tmp &#61; *this;&#43;&#43;*this;return tmp;}bool operator&#61;&#61;(const ListIter& i)const { return ptr &#61;&#61; i.ptr; }bool operator!&#61;(const ListIter& i)const { return ptr !&#61; i.ptr; }
};

  • 由于find()算法内以*iter!&#61;value来检查元素值是否吻合&#xff0c;而本例中的value的类型是int&#xff0c;iter的类型是ListItem&#xff0c;两者不能直接进行比较&#xff0c;因此还需要设计一个全局的operator!&#61;重载函数&#xff0c;并以int和ListItem为参数&#xff1a;

template
bool operator!&#61;(const ListItem& item, T n)
{return (item->value() !&#61; n);
}

  • 现在我们可以使用我们自己的容器与迭代器了&#xff1a;

int main()
{List myList;for (int i &#61; 0; i <5; &#43;&#43;i) {myList.insert_front(i);myList.insert_end(i &#43; 2);}ListIter > begin(myList.front());ListIter > end;ListIter > iter;iter &#61; find(begin, end, 3);if (iter &#61;&#61; end)std::cout <<"3 not found" <}

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

四、迭代器相应类型
  • 前文介绍了迭代器的雏形&#xff0c;在使用迭代器的时候&#xff0c;我们可能需要知道这个迭代器所指之物的数据类型&#xff0c;但是C&#43;&#43;不支持typeof()这种机制&#xff0c;因此我们需要一种技术来获取迭代器所指之物的类型
  • 参数推导机制&#xff1a;参数推导机制可以让我们推断出参数的数据类型

template //此例中&#xff0c;T为int
void func_impl(I iter, T t)
{T tmp;//...
}template
inline void func(I iter)
{func_impl(iter, *iter);
}int main()
{int i;func(&i);
}

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

五、声明内嵌类型
  • 上面介绍了函数的参数推导机制&#xff0c;推导机制虽然好&#xff0c;但是只能获取参数的类型&#xff0c;不能获取函数返回值的类型
  • 作为解决办法&#xff0c;声明内嵌类型是一个好主意&#xff0c;例如&#xff1a;
  • 声明内嵌类型&#xff1a;下面我们在MyIter中使用typedef声明一个别名&#xff0c;这个别名就是传入迭代器的类型的别名。在func函数中&#xff0c;我们返回迭代器中的元素&#xff0c;因此我们需要知道迭代器所指之物的类型才能为其声明返回值类型&#xff0c;此处我们使用了typename来引出迭代器中的类型别名

//迭代器类
template
struct MyIter {typedef T value_type; //内嵌类型别名&#xff0c;用value_type来表示迭代器类所指之物的类型T* ptr;MyIter(T* p &#61; 0) :ptr(p) {}T& operator*()const { return *ptr; }
};/*为什么要使用typename&#xff1a;因为编译器不知道T是什么&#xff0c;也就是说编译器不知道MyIter::value_type是一个类型还是一个成员函数还是一个成员变量&#xff0c;此处我们使用typename告诉编译器这是一种类型&#xff0c;这样才能顺利编译通过
*/
template
typename I::value_type //一整行为func的返回值类型
func(I ite){return *ite;
}MyIter ite(new int(8));
cout <

六、偏特化
  • 上面的声明内嵌类型很好用&#xff0c;但是并不是所有迭代器的都是class&#xff0c;例如原生指针就不是class&#xff08;例如int*&#xff0c;stirng*&#xff09;&#xff0c;那么此时我们就无法使用内嵌类型了&#xff0c;但是STL算法、容器等都是接受原生指针作为一种迭代器的。那么针对于原生指针我们该如何处理呢&#xff1f;
  • 是的&#xff0c;偏特化&#xff08;Paritial Specialization&#xff09;可以做到。
  • 偏特化的概念是&#xff1a;我们可以针对类模板中的template参数进行特化工作。为其提供一个特化版本&#xff08;也就是讲template中参数赋予明确的指定&#xff09;
  • 例如下面是常规的类模板&#xff0c;允许接收T为任何类型

template
class C{};

  • 下面是一个特化版本&#xff0c;允许接收T为原生指针的情况 

template
class C{};

  • 为迭代器声明一个偏特化&#xff1a;在“二”中我们的迭代器没有偏特化版本&#xff0c;因此传入原生指针之后就不可以推导出迭代器所指之物的类型了。有了偏特化&#xff0c;我们就可以对迭代器进行偏特化&#xff0c;设计出下面的迭代器

//偏特化迭代器类
template
struct MyIter {typedef T value_type; //这个迭代器接受原生指针&#xff0c;从而获得指针所指之物的数据类型T* ptr;MyIter(T* p &#61; 0) :ptr(p) {}T& operator*()const { return *ptr; }
};

七、Traits编程技术

  • 通过上面的介绍&#xff0c;我们正式开始介绍Traits编程技术
  • 下面的类模板专门用来“萃取”迭代器的类型&#xff0c;value_type就是迭代器的特性之一&#xff1a;

//如果I为迭代器类&#xff0c;并且迭代器类中声明有类型别名&#xff0c;那么我们就可以萃取出其中的数据类型template
struct iterator_traits {typedef typename I::value_type value_type;
};

  • traits是指&#xff1a;如果I定义有自己的value type&#xff0c;那么我们就可以通过上面设计的iterator_traits类来萃取出I的value_type&#xff08;数据类型&#xff09;。我们上面的func函数还可以更改为&#xff1a;

//萃取的使用场景
template
typename iterator_traits::value_type //一整行为func的返回值类型
func(I ite){return *ite;
}

  • traits也可以有特化版本&#xff1a;例如下面只针对于迭代器是原生指针的偏特化版本&#xff0c;其能萃取出指针所指之物的数据类型

template
struct iterator_traits {typedef T value_type;
};

  • 注意“指向常数对象的指针”特化本&#xff1a;针对“指向常数对象的指针”&#xff0c;下面的式子会得出什么结果呢&#xff1f;

iterator_traits::value_type;

  • 我们希望这种traits机制来获取迭代器的类型&#xff0c;但是如果上面的式子萃取出的是const int类型&#xff0c;那么声明就无法赋值了&#xff0c;因此如果迭代器是个pointer-to-const&#xff0c;我们应该设置下面的特化版本

template
struct iterator_traits {typedef T value_type; //萃取出的是T&#xff0c;而不是const T
};

  • 下图说明了traits所扮演的“特性萃取机”角色&#xff0c;萃取各个迭代器的特性
  • 当然如果希望“特性萃取机”traits能够正常工作&#xff0c;前提是迭代器必须在自己的类中声明类型别名&#xff08;typedef&#xff09;

八、iterator_traits萃取机
  • 下面是迭代器源码中的iterator_traits类&#xff08;其中的一个版本&#xff09;&#xff0c;其用来萃取迭代器中的相应类型

//迭代器的源码
template
struct iterator {typedef Category iterator_category;typedef T value_type; //迭代器所指之物的数据类型(其余的见下面介绍)typedef Distance difference_type;typedef Pointer pointer;typedef Reference reference;
};//traits萃取机&#xff0c;其中I为迭代器类
template
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;
};

  • 当然&#xff0c;iteraotr_traits类还必须设计针对于迭代器是pointer以及pointer-to-const的特化版本&#xff08;见后面一篇文章得迭代器介绍源码&#xff09;
  • 常用的迭代器相应类型有5中&#xff1a;value type、difference type、pointer、reference、iterator catagory&#xff0c;在下面一一介绍

九、迭代器相应类型之&#xff1a;value type
  • 所谓value type&#xff0c;就是指迭代器所指对象的类型
  • 任何一个与STL搭配的迭代器&#xff0c;都应该定义自己的value type内嵌类型

十、迭代器相应类型之&#xff1a;difference type
  • difference type用来表示两个迭代器之间的距离
  • 因此可以用来表示一个容器的最大容量&#xff08;对于连续空间的容器而言&#xff0c;头尾之间的距离就是其最大容量&#xff09;
  • 如果一个泛型算法提供计数功能&#xff0c;例如STL的count()算法&#xff0c;其返回值就必须使用迭代器的difference type

template
typename iterator_traits::difference_type //函数的返回值类型
count(I first, I last, const T& value) {typename iterator_traits::difference n &#61; 0;for (; first !&#61; last; &#43;&#43;first)&#43;&#43;n;return n;
}

  • 针对于difference type的特化版本&#xff1a;针对相应类型difference type&#xff0c;traits设计了&#xff08;针对原生指针&#xff09;下面两个特化版本&#xff0c;以C&#43;&#43;内建的ptrdiff_t作为原生指针的difference type

//针对于原生指针的偏特化版本
template
struct iterator_traits {//..typedef ptrdiff_t difference_type;//...
};//针对于pointer-to-const的偏特化版本
template
struct iterator_traits {//..typedef ptrdiff_t difference_type;//...
};

十一、迭代器相应类型之&#xff1a;reference type

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

  • 如果想要传回左值&#xff0c;都是以by reference的方式进行&#xff08;引用&#xff09;&#xff0c;所以如果迭代器是mutable iterators类型的&#xff0c;那么其value type是T&#xff0c;*p返回的不应该是T&#xff0c;而应该是T&&#xff08;引用&#xff09;
  • 源码如下&#xff1a;

template
struct iterator_traits {typedef typename I::reference reference;
};//针对于原生指针设计的偏特化版本
template
struct iterator_traits {typedef T& reference;
};//针对于pointer-to-const设计的偏特化版本
template
struct iterator_traits {typedef const T& reference;
};

十二、迭代器相应类型之&#xff1a;pointer type

  • pointer type传回一个指针&#xff0c;指向于迭代器所指之物。源码如下&#xff1a;

template
struct iterator_traits {typedef typename I::pointer pointer;
};//针对于原生指针设计的偏特化版本
template
struct iterator_traits {typedef T* pointer;
};//针对于pointer-to-const设计的偏特化版本
template
struct iterator_traits {typedef const T* pointer;
};

十三、迭代器相应类型之&#xff1a;iterator_category

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

  • 设计算法时&#xff0c;如果可能尽量针对图中的某种迭代器提供一个明确定义&#xff0c;并针对更强化的某种迭代器提供另一种定义&#xff0c;这样才能在不同的情况下提供最大效率&#xff1a;假设有一个算法可接受Forward Iterator&#xff0c;如果你以一个Random Access Iterator换递给它当然可以&#xff0c;但是Random Access Iterator不是最佳的选择
  • 下面是五个类&#xff0c;用来表示五种迭代器的类型&#xff0c;类中不需要任何成员&#xff0c;至于为什么使用继承机制&#xff08;下面介绍&#xff09;&#xff1a;

//用于标记迭代器的类型
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 {};

  • 将迭代器类进行继承的好处是&#xff1a;我们可以不必再写“单纯只做传递调用”的函数&#xff08;例如下面要介绍的advance() ForwardIterator八本&#xff09;。考虑下满的例子&#xff0c;从其中的输出可以看到如下的端倪&#xff1a;

#include using namespace std;struct B {}; //B模拟InputIterator
struct D1 :public B {}; //D1模拟ForwardIterator
struct D2 :public D1 {};//D2模拟BidirectionalIteratortemplate
void func(I& p, B)
{std::cout <<"B version" <}template
void func(I& p, D2)
{std::cout <<"D2 version" <}int main()
{int *p;func(p, B()); //参数匹配&#xff0c;输出“B version”func(p, D1()); //参数不匹配&#xff0c;因继承关系输出“B version”func(p, D2()); //参数匹配&#xff0c;输出“D2 version”return 0;
}

  • 以advance()函数为例&#xff0c;介绍什么是iterator_category&#xff1a;advance()函数是一个内部算法&#xff0c;该函数有两个参数&#xff1a;一个是迭代器p和数值n&#xff08;用来将迭代器p进行n次前进&#xff09;。下面是advance的三份定义&#xff1a;
    • 一份针对于Bidirectional Iterator
    • 一份针对于Random Access Iterator
    • 一份针对于Forward Iterator

template
void advance_II(InputIterator& i, Distance n)
{//单向&#xff0c;逐一前进while (n--)&#43;&#43;i;
}template
void advance_BI(BidirectionalIterator& i, Distance n)
{//双向&#xff0c;逐一前进if (n >&#61; 0)while (n--)&#43;&#43;i;elsewhile (n&#43;&#43;)--i;
}template
void advance_RAI(RandomAccessIterator& i, Distance n)
{//双向&#xff0c;跳跃前进i &#43;&#61; n;
}

  • 我们有三份advance()&#xff0c;那么我们调用的时候调用哪种类型的advance呢&#xff1f;
  • 我们可以使用重载机制&#xff0c;在上面的advance()函数中添加第三个参数&#xff0c;用来表示该advance()函数所适用的迭代器类型&#xff0c;此时可以考虑使用trsits的萃取功能萃取出迭代器的类型
  • 现在重新定义__advance()函数&#xff0c;添加第三个参数代表迭代器的类型&#xff0c;如下所示&#xff1a;

template
void __advance(InputIterator& i, Distance n,input_iterator_tag)
{//单向&#xff0c;逐一前进while (n--)&#43;&#43;i;
}//这是一个单纯的传递调用函数&#xff0c;在上面迭代器继承中介绍如何去除它
template
void __advance(RandomAccessIterator& i, Distance n,forword_iterator_tag)
{advance(i,n,input_iterator_tag());
}template
void __advance(BidirectionalIterator& i, Distance n,bidirectional_itertator_tag)
{//双向&#xff0c;逐一前进if (n >&#61; 0)while (n--)&#43;&#43;i;elsewhile (n&#43;&#43;)--i;
}template
void __advance(RandomAccessIterator& i, Distance n,random_access_iterator_tag)
{//双向&#xff0c;跳跃前进i &#43;&#61; n;
}

  • 现在创建一个上层接口&#xff0c;该接口接受一个迭代器&#xff0c;然后通过萃取迭代器中的iterator_category()来获得迭代器类型&#xff0c;然后再根据迭代器的类型调用上面的不同__advance()函数

//备注&#xff1a;此函数的命名以InputIterator为名&#xff0c;因为InputIterator为最低阶迭代器类型
template
void __advance(InputIterator& i, Distance n, input_iterator_tag)
{__advance(i, n, iterator_traits::iterator_category());
}

  • 上述代码中的iterator_traits::iterator_category()将产生一个临时对象&#xff0c;其类型是上面介绍的五种迭代器类型之一&#xff0c;然后根据这个类型&#xff0c;编译器决定调用哪一种__advance()函数
  • 关于上面的函数&#xff0c;SGI STL中的源码是&#xff1a;

  • 因此&#xff0c;为了满足上面的行为&#xff0c;traits必须再增加一个相应类型&#xff1a;

template
struct iterator_traits {typedef typename I::iterator_category interator_category;
};//针对原生指针设计的偏特化版本
template
struct iterator_traits {//注意&#xff0c;原生指针是一种Random Access Iteratortypedef random_access_iterator_tag interator_category;
};//针对于pointer-to-const设计的偏特化版本
template
struct iterator_traits {typedef random_access_iterator_tag interator_category;
};

  • 以distance()函数为例&#xff0c;介绍什么是iterator_category&#xff1a;上面以advance()函数介绍了iterator_category的运用场景&#xff0c;下面介绍在distance()函数中的应用场景。distance()函数用来计算两个迭代器之间的距离。根据迭代器种类的不同&#xff0c;计算的方式也不同。下面是两种__distance()的函数定义

template
inline iterator_traits::difference_type
__distance(InputIterator first, InputIteratorlast last,input_iterator_tag)
{iterator_traits::difference_type n &#61; 0;//逐一累计距离while (first !&#61; last) {&#43;&#43;first; &#43;&#43;n;}return n;
}template
inline iterator_traits::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,random_access_iterator_tag)
{//直接计算差距return last - first;
}

  • 下面定义一个顶层接口&#xff0c;根据传入迭代器&#xff0c;萃取其类型&#xff0c;然后调用不同种类的__distance()函数&#xff1a;

template
inline iterator_traits::difference_type
distance(InputIterator first, InputIteratorlast last,input_iterator_tag)
{//萃取出迭代器的类型typedef typename iterator_traits::iterator_category category;return __distance(first, last, category()));
}

  •  注意事项&#xff1a;
    • distance()函数可接受任何类型的迭代器&#xff0c;在上面介绍了迭代器的继承&#xff0c;当传入的迭代器为Oupt Iterator或Forward Iterator或Bidirectional Iterator时&#xff0c;回去调用Input Iterator版本的__distance()函数
    • 而传入RandomAccessIterator时&#xff0c;调用的就是RandomAccessIterator版本的__distance()版本的函数

十四、traits技术得保证
  • 为了使迭代器支持traits技术&#xff0c;STL提供了一个iterator类&#xff0c;每个新设计的迭代器都要继承于这个iterato类&#xff0c;iterator类中不含任何成员&#xff0c;纯粹都是类型定义&#xff0c;后面三个参数都有默认值&#xff0c;因此新的迭代器只需提供前两个参数即可&#xff1a;

template
struct iterator {typedef Category iterator_category;typedef T value_type;typedef Distance difference_type;typedef Pointer pointer;typedef Reference reference;
};

  • 有了上面的类之后&#xff0c;我们的iterator_traits类就可以轻易的萃取出迭代器的类型了
  • 例如&#xff0c;下面我们将前一篇文章中我们自定义的迭代器ListIter进行改写&#xff0c;使其继承于iterator&#xff1a;

template
struct ListIter :public std::iterator
{};

十五、iterator源代码实例

  • 下面列出了SGI STL头文件的代码
  • 关于iostream iterators、inserter iterators、reverse iterators的实现&#xff0c;在后面配接器中介绍
  • 迭代器类型&#xff1a;

//用于标记迭代器的类型
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&#xff1a;为了防止代码出错&#xff0c;自己定义的迭代器最好都继承这个类

template
struct iterator {typedef Category iterator_category;typedef T value_type;typedef Distance difference_type;typedef Pointer pointer;typedef Reference reference;
};

  • traits萃取机&#xff1a;

//萃取机traits
template
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
struct iterator_traits {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
struct iterator_traits {typedef random_access_iterator_tag iterator_category;typedef T value_type;typedef ptrdiff_t difference_type;typedef const T* pointer;typedef const T& reference;
};

  • 下面这个函数可以方便的萃取出某个迭代器的类型&#xff08;category&#xff09;

//参数为迭代器
template
inline typename iterator_traits::iterator_categoryiterator_category(const Iterator&)
{typedef typename iterator_traits::iterator_category category;return category();
}

  •  下面这个函数可以方便的萃取出某个迭代器的distance type

//参数为迭代器
template
inline typename iterator_traits::difference_type*distance_type(const Iterator&)
{return static_cast::difference_type*>(0);
}

  • 下面这个函数可以方便的萃取出某个迭代器的value type

//参数为迭代器
template
inline typename iterator_traits::value_type*distance_type(const Iterator&)
{return static_cast::value_type*>(0);
}

  • 整组distance函数&#xff1a;

template
inline iterator_traits::difference_type
__distance(InputIterator first, InputIteratorlast last,input_iterator_tag)
{iterator_traits::difference_type n &#61; 0;while (first !&#61; last) {&#43;&#43;first; &#43;&#43;n;}return n;
}template
inline iterator_traits::difference_type
__distance(RandomAccessIterator first, RandomAccessIterator last,random_access_iterator_tag)
{return last - first;
}template
inline iterator_traits::difference_type
distance(InputIterator first, InputIteratorlast last,input_iterator_tag)
{typedef typename iterator_traits::iterator_category category;return __distance(first, last, category()));
}

  • 整组advance函数&#xff1a;

template
void __advance(InputIterator& i, Distance n,input_iterator_tag)
{//单向&#xff0c;逐一前进while (n--)&#43;&#43;i;
}template
void __advance(BidirectionalIterator& i, Distance n,bidirectional_itertator_tag)
{//双向&#xff0c;逐一前进if (n >&#61; 0)while (n--)&#43;&#43;i;elsewhile (n&#43;&#43;)--i;
}template
void __advance(RandomAccessIterator& i, Distance n,random_access_iterator_tag)
{//双向&#xff0c;跳跃前进i &#43;&#61; n;
}template
void __advance(InputIterator& i, Distance n, input_iterator_tag)
{__advance(i, n, iterator_category(i));
}

十六、__type_traits概述

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

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

  • 当我们有了对象的数据类型之后&#xff0c;又萃取出这个数据类型是否具有上面的那些特性&#xff0c;那么我们就可以调用不同的处理方法来处理对象

十八、__typr_traits的使用方法
  • 例如我们可以使用下面的方法使用__type_traits&#xff0c;其中T代表任意的数据类型&#xff1a;

//下面的式子应该返回真或假&#xff0c;然后好让我们进一步判断
__type_traits::has_trivial_default_constructor;
__type_traits::has_trivial_copy_constructor;
__type_traits::has_trivial_assignment_operator;
__type_traits::has_trivial_destructor;
__type_traits::is_POD_type;

  • 对于上面的式子&#xff0c;我们希望对传入的数据类型T&#xff0c;萃取出其是否具有non-trivial构造/拷贝构造等也就是上面的式子应该返回“真”或“假”
  • 但是我们不希望上面的代码返回bool值&#xff0c;而是返回类&#xff0c;于是STL中定义了下面的两个类&#xff0c;用来表示“真”或“假”&#xff1a;

//没有任何数据成员&#xff0c;单纯的用来表示“真”或“假”
struct __true_type {}; //真
struct __false_type {}; //假

  • __type_traits类的定义&#xff1a;__type_traits就是用来萃取对传入的数据类型是否具有trivial构造函数/拷贝构造/赋值运算符/析构函数/POD性质等

template
struct __type_traits {typedef __true_type this_dummy_must_be_first;/*不要移除这个成员&#xff0c;它通知“有能力自动将__type_traits特化”的编译器说&#xff0c;我们现在所看到的这个__type_traits_template是特殊的。这是确保万一编译器也使用一个名为__type_traits而其实与此处定义并无任何关联的template时&#xff0c;所有事情可以顺序编译通过*//*下面的成员可以自己添加/删除&#xff0c;用来表示某种数据类型是否具有某种特性*/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最保守的值&#xff0c;然后在下面会看到针对每一个数据类型又设计了适当的__type_traits特化版本
  • 下面是中对所有C&#43;&#43;类型定义的__type_traits的特化版本

/*下面是C&#43;&#43;针对基本数据类型char、signed char、unsigned char、short、unsigned short、int、unsigned int、long、unsigned long、float、double、long提供的特化版本注意&#xff1a;每一个成员都是__true_type的&#xff0c;因此这些数据类型可以用最快速的方法&#xff08;memcpy&#xff09;来进行拷贝或赋值操作__STL_TEMPLATE_NULL是定义在中的&#xff0c;定义为template<>
*/template
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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
__STL_TEMPLATE_NULL struct __type_traits {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()全局函数&#xff1a;

template
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的数据类型&#xff0c;见前一篇文章

  • 该函数以x为蓝本&#xff0c;自迭代器first开始构造n个元素
  • 下面是__uninitialized_fill_n()函数的定义

template
inline ForwardIterator __uninitialized_fill_n(ForwardIterator first,Size n, const T& x,T1*)
{//萃取数据类型T1是否具有POD特性typedef typename __type_traits::is_POD_type is_POD;//然后调用该函数return __uninitialized_fill_n_aux(first, n, x, is_POD());
}

  • 函数中萃取出数据类型中是否具有POD性质&#xff0c;于是会返回__false_type类或者__true_type类&#xff0c;然后将类传入 __uninitialized_fill_n_aux()函数的第三个参数中
  • __uninitialized_fill_n_aux()函数根据传入的类类型&#xff0c;然后再调用不同的版本

//不具有POD性质的函数
template
inline ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first,Size n, const T& x, __false_type)
{ForwardIterator cur &#61; first;//为了阅读方便&#xff0c;此处省略了异常处理for (; n > 0; --n, &#43;&#43;cur)construct(&*cur, x);return cur;
}//具有POD性质的函数
//如果拷贝构造函数等同于拷贝赋值运算符&#xff0c;而且有trivial析构函数&#xff0c;那么下面就有效
template
inline ForwardIterator __uninitialized_fill_n_aux(ForwardIterator first,Size n, const T& x, __true_type)
{return fill_n(first, n, x);
}
//定义于
template
OutputIterator fill_n(OutputIterator first,Size n, const T& val)
{for (; n > 0; --n, &#43;&#43;first)*first &#61; value;return first;
}

二十、__type_traits实际运用②

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

二十一、__type_traits实际运用③
  • copy()算法解析参阅&#xff1a;https://blog.csdn.net/qq_41453285/article/details/104285962
  • copy()算法有非常多的特化与强化版本&#xff0c;根据不同的数据类型调用不同的方法来进行拷贝
  • 下面是指简略版本&#xff0c;详细介绍请参阅上面的链接
  • 源码&#xff1a;下面是copy的底层实现&#xff0c;它会萃取参数是否具有trivial拷贝构造函数&#xff0c;然后调用不同的版本

template
inline void copy(T* source, T* destination, int n)
{copy(source, destination, n, typename __type_traits::has_trivial_copy_constructor());
}

  • 下面是copy()的底层实现

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

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

  • 下面是中的声明&#xff1a;

  • 因此&#xff0c;你可以在自己的程序中充分利用这个__type_traits机制
  • 演示案例&#xff1a;例如你自己定义了一个Shape类&#xff0c;那么如果你将Shape类传入__type_traits&#xff0c;默认情况下&#xff0c;萃取出的特性都是__false_type的&#xff0c;因为在“三”中我们定义的__type_traits原型来说&#xff0c;你的Shape没有特化版本&#xff0c;默认的情况下全部返回“假”&#xff08;也就是返回__false_type类&#xff09;。因此&#xff0c;你可以自己设计一个__type_traits特化版本&#xff0c;针对于你自己的类型

template
__STL_TEMPLATE_NULL struct __type_traits {typedef __true_type this_dummy_must_be_first;//下面的内容自己定义&#xff08;为真还是为假&#xff09;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&#xff0c;参阅上面的介绍。



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

 


推荐阅读
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 如何在跨函数中使用内存?
    本文介绍了在跨函数中使用内存的方法,包括使用指针变量、动态分配内存和静态分配内存的区别。通过示例代码说明了如何正确地在不同函数中使用内存,并提醒程序员在使用动态分配内存时要手动释放内存,以防止内存泄漏。 ... [详细]
author-avatar
rhp3465483
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有