前言
在上一小节中,我们提到了迭代器的相应型别,其中之一的便是value_type
,它是根据函数模板的参数推导机制获取到的。但是需要考虑一种另外的情况,当数据类型不是对象的时候,这种时候很明显无法再在类中typedef T value_type
了,而是一个普通的int *
指针,那么我们如何得到它的value_type
。只用将原生指针进行特殊处理就行了(模板的偏特化),这里就引入了traits,专门用来获取到迭代器的相关类型。
iterator_traits
下面直接看它的源码,里面用到了模板偏特化,大意就是可以在模板泛化中设计一个特别的版本,即将某种template参数指定明确的类型等。
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
/* 这样看来只是多了一层间接性
* 因为本来Iterator中就有这些类型,还要放在iterator_traits重新typedef一次
* 这样做的目的就是为了特化的版本(为原生指针提供其相应类型)
*/
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;
};
/* 特化版本,迭代器是个原生指针
* 这样就能将原生指针的相应型别获取到了
* 可以看到原生指针的iterator_category是random_access_iterator_tag型的
*/
template <class T>
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;
};
/* 这里还要针对const T*特化
* 否则还是依照上一个特化版本的话,得到的是value_type是const int而不是int
*/
template <class T>
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;
};
/* 这里用到了一个typename,它的作用是声明当前的iterator_traits是一种类型
* 这个在effectiveC++里面交代的很清楚
* 在前面一节中我们看到了iterator_category的用法
* 这里可以很直观的看到它就是构造并返回了当前迭代器的iterator_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();
}
/* 这里用到了0可以转换成指针的性质
* 相当于返回一个空指针
*/
template <class Iterator>
inline typename iterator_traits<Iterator>::difference_type*
distance_type(const Iterator&) {
return static_cast<typename iterator_traits<Iterator>::difference_type*>(0);
}
template <class Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&) {
return static_cast<typename iterator_traits<Iterator>::value_type*>(0);
}
这样一来,我们就可以获取到任何类型的相应型别了,之所以使用traits获取这些类型,也是为了将STL的效率提升到极至。比如在上一小节举的关于distance
函数的例子,这里我们再把advance
的函数列出感受一下traits
带给我们的好处:
/* advance调用iterator_category获取到迭代器的类型,根据类型的不同,选择效率最高的方式
* 比如类型是random_access_iterator_tag时,就可以直接对迭代器进行跳跃移动,不需要循环依次移动
*/
template <class InputIterator, class Distance>
inline void __advance(InputIterator& i, Distance n, input_iterator_tag) {
while (n--) ++i;
}
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma set woff 1183
#endif
template <class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator& i, Distance n,
bidirectional_iterator_tag) {
if (n >= 0)
while (n--) ++i;
else
while (n++) --i;
}
#if defined(__sgi) && !defined(__GNUC__) && (_MIPS_SIM != _MIPS_SIM_ABI32)
#pragma reset woff 1183
#endif
template <class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator& i, Distance n,
random_access_iterator_tag) {
i += n;
}
template <class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n) {
__advance(i, n, iterator_category(i));
}
type_traits
SGISTL将traits扩展到了可以获取类型的特性,比如该类型是否有non-trivial assignment等函数,这样就可以采取适合对当前对象比较有效率的做法。比如如果是trivial拷贝构造函数,那么直接调用memcpy
就行了,而不用去调用它。
它主要可以获取到对象如下的特性:
- has_trivial_default_constructor:是否有无用的默认构造函数
- has_trivial_copy_constructor:是否有无用的默认拷贝构造函数
- has_trivial_assignment_operator:是否有无用的赋值函数
- has_trivial_destructor:是否有无用的析构函数
- is_POD_type:plain old data,是指标量类型或C的struct类型,POD类别必有trivial ctor/dtor/copy/assignment函数
该对象的类型是否具有这些特性,对应了true
或false
,但是我们不能将其设为bool值,因为我们需要在编译期就决定该使用哪个函数,所以需要利用函数模板的参数推导机制。因为true
或false
,我们将其表现为一个空类。
源码如下
/* __true_type和__false_type,两个空类 */
struct __true_type {
};
struct __false_type {
};
/* 将基础类型的特性特化成__true_type */
template <class type>
struct __type_traits {
typedef __true_type this_dummy_member_must_be_first;
/* Do not remove this member. It informs a compiler which
automatically specializes __type_traits that this
__type_traits template is special. It just makes sure that
things work if an implementation is using a template
called __type_traits for something unrelated. */
/* The following restrictions should be observed for the sake of
compilers which automatically produce type specific specializations
of this class:
- You may reorder the members below if you wish
- You may remove any of the members below if you wish
- You must not rename members without making the corresponding
name change in the compiler
- Members you add will be treated like regular members unless
you add the appropriate support in the compiler. */
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;
};
// Provide some specializations. This is harmless for compilers that
// have built-in __types_traits support, and essential for compilers
// that don't.
/* 针对char类型特化 */
__STL_TEMPLATE_NULL struct __type_traits<char> {
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;
};
/* 针对signed char类型特化 */
__STL_TEMPLATE_NULL struct __type_traits<signed char> {
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;
};
/* 针对unsigned char类型特化 */
__STL_TEMPLATE_NULL struct __type_traits<unsigned char> {
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;
};
/* 针对short类型特化 */
__STL_TEMPLATE_NULL struct __type_traits<short> {
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;
};
/* 针对unsigned char类型特化 */
__STL_TEMPLATE_NULL struct __type_traits<unsigned short> {
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;
};
...
//上面都是对基本数据类型进行特化,就不贴出全部代码了,有点多
//下面是针对指针进行特化,基本上也是将基础的数据类型的特性置为__true_type
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
template <class T>
struct __type_traits<T*> {
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;
};
#else /* __STL_CLASS_PARTIAL_SPECIALIZATION */
struct __type_traits<char*> {
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;
};
struct __type_traits<signed char*> {
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;
};
struct __type_traits<unsigned char*> {
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;
};
#endif /* __STL_CLASS_PARTIAL_SPECIALIZATION */
可以看到,针对数据类型是基本数据类型的情况,所有特性都是__true_type
,但是如果是针对对象,很有可能全部的特性都被置成__false_type
,不管它是否是POD型,这跟编译器有关,你也可以自己添加代码在这个头文件中,表明你的对象的特性。
小结
在本小节中,主要分析了traits
编程技法以及如何获取迭代器的五种相应型别,如果是对象,则可以依据模板的参数推导机制来获取,但是如果是基础的数据类型,只有使用模板的偏特化。这样我们便可以通过迭代器取得关于对象的数据类型等信息,这样做的其中一个好处就是可以提高一些算法的效率。
比如我们可以使用value_type
获取到迭代器指向的对象的数据类型,再使用__type_traits
机制,判断该对象的某个特性是__true_type
还是__false_type
而采取最佳方法。需要注意的是,这些取得的数据类型之类的,都是以对象的形式,利用模板的参数推导在编译期就已经决定了具体使用哪个函数。