STL迭代器
在 STL 编程中,容器和算法是独立设计的,即数据结构和算法是独立设计的,连接容器和算法的桥梁就是迭代器了:
迭代器是一种行为类似指针的对象,而指针的各种行为中最常见也最重要的使用是:内容提领 和成员访问,所以迭代器会对operator* 和 operator-> 进行重载工作,首先来看下list容器的迭代器实现:
template<typename _Tp>
struct _List_iterator
{
typedef _List_iterator<_Tp> _Self;
typedef _List_node<_Tp> _Node;
typedef ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
typedef _Tp value_type;
typedef _Tp* pointer;
typedef _Tp& reference;
_List_iterator() _GLIBCXX_NOEXCEPT
: _M_node() { }
explicit
_List_iterator(__detail::_List_node_base* __x) _GLIBCXX_NOEXCEPT
: _M_node(__x) { }
_Self
_M_const_cast() const _GLIBCXX_NOEXCEPT
{ return *this; }
// Must downcast from _List_node_base to _List_node to get to _M_data.
reference
operator*() const _GLIBCXX_NOEXCEPT
{ return static_cast<_Node*>(_M_node)->_M_data; }
pointer
operator->() const _GLIBCXX_NOEXCEPT
{ return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); }
_Self&
operator++() _GLIBCXX_NOEXCEPT
{
_M_node = _M_node->_M_next;
return *this;
}
_Self
operator++(int) _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
_M_node = _M_node->_M_next;
return __tmp;
}
_Self&
operator--() _GLIBCXX_NOEXCEPT
{
_M_node = _M_node->_M_prev;
return *this;
}
_Self
operator--(int) _GLIBCXX_NOEXCEPT
{
_Self __tmp = *this;
_M_node = _M_node->_M_prev;
return __tmp;
}
bool
operator==(const _Self& __x) const _GLIBCXX_NOEXCEPT
{ return _M_node == __x._M_node; }
bool
operator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT
{ return _M_node != __x._M_node; }
// The only member points to the %list element.
__detail::_List_node_base* _M_node;
};
当使用上述定义的迭代器时,可能需要获取迭代器所指之物的型别(比如:_Tp),有如下情形:
1. function template 的参数推导
#include <iostream>
#include <list>
using namespace std;
template <class I>
inline
void func(I iter) {
func_impl(iter, *iter);
}
template <class I, class T>
void func_impl(I iter, T t) {
T tmp; // 通过类型推导来获得所指之物的型别
cout << "func_impl" << endl;
}
int main()
{
list<int> mList;
mList.push_back(1);
mList.push_back(2);
mList.push_back(3);
func(mList.begin());
return 0;
}
2. 使用声明的内嵌型别
函数的"template 参数推导机制"推导的只是参数,无法推导函数的返回值类型,如果返回值需要使用迭代器所指对象的型别,我们需要使用迭代器的 value type:
template <class I>
inline
typename I::value_type
func_back(I iter) {
return *iter;
}
int main()
{
list<int> mList;
mList.push_back(1);
mList.push_back(2);
mList.push_back(3);
cout << " func_back value= " << func_back(mList.begin()) << endl;;
return 0;
}
上述的函数利用了迭代器类的内嵌型别作为返回类型,这个时候是正常执行的,但是如果 I 是一个原生的指针,是不能声明内嵌型别的,这个时候就要做偏特化处理:
template <class I>
inline
I func_back(I* iter) {
cout << "func_back 2" << endl;
return *iter;
}
整个测试程序:
#include <iostream>
#include <list>
using namespace std;
template <class I>
inline
void func(I iter) {
func_impl(iter, *iter);
}
template <class I, class T>
void func_impl(I iter, T t) {
T tmp; // 通过类型推导来获得所指之物的型别
cout << "func_impl" << endl;
}
template <class I>
inline
typename I::value_type
func_back(I iter) {
cout << "func_back 1" << endl;
return *iter;
}
template <class I>
inline
I func_back(I* iter) {
cout << "func_back 2" << endl;
return *iter;
}
int main()
{
list<int> mList;
mList.push_back(1);
mList.push_back(2);
mList.push_back(3);
func(mList.begin());
cout << " func_back value= " << func_back(mList.begin()) << endl;;
cout << " func_back2 value= " << func_back(&mList.front()) << endl;;
return 0;
}
输出:
Traits 编程技法
上面通过函数偏特化的方式,来实现内嵌型别的使用,在STL中通过利用中间层 iterator_traits 来固定func的形式,减少代码的重复,唯一要做的就是为 iterator_tartis 做偏特化使其支持 pointer 和 const pointer(如果迭代器是个pointer to const,应该设法令其value_type
为non-const,否则声明一个无法赋值的临时变量是没有意义的),而并不需要对func进行偏特化处理:
下面通过中间层traita来修改上述的测试程序:
template <class T>
struct my_iterator_traits {
typedef typename T::value_type value_type;
};
template <class T>
struct my_iterator_traits<T*> {
typedef T value_type;
};
template <class T>
struct my_iterator_traits<const T*> {
typedef T value_type;
};
template <class I>
inline
typename my_iterator_traits<I>::value_type
func_use_traits(I iter) {
cout << "func_use_traits" << endl;
return *iter;
}
int main()
{
list<int> mList;
mList.push_back(1);
mList.push_back(2);
mList.push_back(3);
//func(mList.begin());
//cout << " func_back value= " << func_back(mList.begin()) << endl;;
//cout << " func_back2 value= " << func_back(&mList.front()) << endl;
cout << " func_use_traits value= " << func_use_traits(mList.begin()) << endl;;
cout << " func_use_traits2 value= " << func_use_traits(&mList.front()) << endl;
return 0;
}
输出结果和上面是一样的,但是这里并没有对func进行偏特化处理如下:
STL iterator源码
迭代器有常见有五种类型: value_type, difference_type, reference_type, pointer_type都比较容易在 traits 和相应偏特化中提取。
template<typename _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;
};
对 pointer 和 const pointer的偏特化:
/// Partial specialization for pointer types.
template<typename _Tp>
struct iterator_traits<_Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};/// Partial specialization for const pointer types.
template<typename _Tp>
struct iterator_traits<const _Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
但是,iterator_category一般也有5个,这个相应型别会引发较大规模的写代码工程。
- 单向移动只读迭代器 Input Iterator
- 单向移动只写迭代器 Output Iterator
- 单向移动读写迭代器 Forward Iterator
- 双向移动读写迭代器 Bidirectional Iterator
- 所有指针的算术能力 Random Access Iterator
例如:我们实现了 advanceII, advanceBI, advanceRAI 分别代表迭代器类型是Input Iterator,Bidirectional Iterator和Random Access Iterator的对应实现:
template<class Iterator>
void advance(Iterator& i) {
if (is_random_access_iterator(i))
advanceRAI(i,n);
if (is_bidirectional_iterator(i))
advanceBI(i,n);
else
advanceII(i,n);
}
但这样在执行时期才决定使用哪一个版本,会影响程序效率。最好能够在编译期就选择正确的版本;而重载这个函数机制可以达成这个目标;而对于advanceXX()
都有两个函数参数,型别都未定(因为都是模板参数)。为了令其同名,形成重载函数,我们必须加上一个型别已确定的函数参数,使函数重载机制得以有效运作起来;如果traits有能力萃取出迭代器的种类,我们便可利用这个"迭代器类型"相应型别作为advancexx的第三个参数,而这个相应型别必须是一个class type,不能只是数值号码类的东西,因为编译器需依赖它来进行重载决议。
STL 中定义如下tag 的类型,用于上述描述形成的重载关系:
/// Marking input iterators.
struct input_iterator_tag { };/// Marking output iterators.
struct output_iterator_tag { };/// Forward iterators support a superset of input iterator operations.
struct forward_iterator_tag : public input_iterator_tag { };/// Bidirectional iterators support a superset of forward iterator
/// operations.
struct bidirectional_iterator_tag : public forward_iterator_tag { };/// Random-access iterators support a superset of bidirectional
/// iterator operations.
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
//@}
以下为以 advance函数,来理解iterator_category形成的重载:
//以下为私有的重载andvance函数
// input iterator
template<class inputIterator, class distance>
inline void __advance(inputIterator&i, distance n,
input_iterator_tag) {
std::cout << "input tag" << std::endl;
}
// output iterator
template<class outputIterator, class distance>
inline void __advance(outputIterator&i, distance n,
output_iterator_tag) {
std::cout << "output tag" << std::endl;
}
// forward iterator
template<class ForwardIterator, class Distance>
inline void __advance(ForwardIterator &i, Distance n,
forward_iterator_tag) {
std::cout << "forward tag" << std::endl;
}
// bidrectional iterator
template<class BidiectionalIterator, class Distance>
inline void __advance(BidiectionalIterator &i, Distance n,
bidiectional_iterator_tag) {
std::cout << "bidrectional tag" << std::endl;
}
// RandomAccess iterator
template<class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator &i, Distance n,
random_access_iterator_tag) {
std::cout << "randomaccess tag" << std::endl;
}
//对外暴露接口
template<class InputIterator, class Distance>
inline void advance(InputIterator &i, Distance n) {
// 通过Ierator_traits询问它的iterator_category是谁
typedef typename Iterator_traits<InputIterator>::iterator_category category;
__advance(i, n, category()); // 各型别的重载
}
//使用
int main() {
iterator<input_iterator_tag> input;
iterator<output_iterator_tag> output;
iterator<forward_iterator_tag> forward;
iterator<bidiectional_iterator_tag> bidect;
iterator<random_access_iterator_tag> random;
advance(input, 10);
advance(output, 10);
advance(forward, 10);
advance(bidect, 10);
advance(random, 10);
return 0;
}
STL提供了一个iterators class 如下,如果每个新设计的迭代器都继承自它,那就可保证符合STL的规范:
/**
* @brief Common %iterator class.
*
* This class does nothing but define nested typedefs. %Iterator classes
* can inherit from this class to save some work. The typedefs are then
* used in specializations and overloading.
*
* In particular, there are no default implementations of requirements
* such as @c operator++ and the like. (How could there be?)
*/
template<typename _Category, typename _Tp, typename _Distance = ptrdiff_t,
typename _Pointer = _Tp*, typename _Reference = _Tp&>
struct iterator
{
/// One of the @link iterator_tags tag types@endlink.
typedef _Category iterator_category;
/// The type "pointed to" by the iterator.
typedef _Tp value_type;
/// Distance between iterators is represented as this type.
typedef _Distance difference_type;
/// This type represents a pointer-to-value_type.
typedef _Pointer pointer;
/// This type represents a reference-to-value_type.
typedef _Reference reference;
};
以上参考: