前面说到,利用模板的参数推导机制可以推导参数,但是如果我们需要该类型的返回值作为参数,则无法通过这个方法获取,这个时候,就需要内建类型。 什么是内建类型呢?我们还用程序来举例子:
template <typename T>class myIterator{ ...};这样一个模板类里,我们需要获取T的类型,可以利用typedef
template <typename T>class myIterator{ typedef T value_type; ...};这样,当我们需要T的类型作为函数的返回值的时候,可以这么写
template<typename T>typename T::value_type func(T x){};注意这里的typename T::value_type
是函数的返回值类型,这里用typename
关键字,也是因为如果不加的话,编译器不知道T::value_type
是一个类型还是一个成员变量。
这样,我们就可以利用这个特性来写iterator
的萃取器trait
了。 看好下面这个代码
刚才的函数就可以写成
template<class T>typename iterator_traits<T>::value_type func(T x){};即如果你这个模板T一旦定义了内嵌类型value_type
,你就可以使用iterator_traits
将其value_type
萃取出来,这么多一层包裹的意义是什么呢?前面说过,原生指针类型必须是iterator
,比方说int*
,它并没有定义value_type
,就无法做到模板的统一了,那么这里迭代器的traits就利用了模板的偏特化这样一个特性,定义了原生指针的value_type
。 什么是偏特化?简单点说: template<class T,class I>
是基础版本,那么 template<class T,int>
就是偏特化版本,而 template<class T,class I*>
也是偏特化版本, template<char, int>
就是全特化版本了。 编译器对模板对号入座的时候,先考虑全特化,再考虑偏特化,最后考虑基础版本。说到这里,我们就可以让原生指针的萃取器trait
变成一个偏特化的版本:
这样就解决了原生指针的问题。
迭代器通常需要定义以下几个type:
value_type;difference_type;pointer;reference;iterator_category;第一个不用说了;第二个定义了表示两个迭代器之间“距离”这样一个东西的类型;第三个和第四个可以用以下两个式子来解释Item* Operator->() const {return ptr;} Item& operator*() const {return *ptr;}
前者返回值就是pointer,后者就是reference;第五个是指迭代器的类型,有以下几种: 1. input iterator 2. output iterator 3. forward iterator 4. bidirectional iterator 5. random access iterator
原生指针是第五个种类哦。 这里的五个级别,以最强的那个为准,当你针对迭代器写函数的时候,如果它是random access iterator,那么它一定也是1、3、4或者2、3、4,那么针对它写的偏特化版本的函数模板,就不一定适用于1、2、3、4,因为它级别最高。
下一篇将讲述一些基本的容器。
新闻热点
疑难解答