std::iterator 与 std::iterator_traits
std::iterator
std::iterator
是一个模板类,其声明为:
template<
class Category,
class T,
class Distance = std::ptrdiff_t,
class Pointer = T*,
class Reference = T&
> struct iterator;
std::iterator
是为简化迭代器所需类型的定义而提供的基类。也就是说当我们写一个模板类时,需要定义自己的迭代器 iterator
,那么我们可以将 std::iterator
作为自定义迭代器的基类。
std::iterator
的模板参数:
- Category: 类型为
iterator_category
,表示迭代器的种类,共有5类:
- T :类型为
value_type
, 可以通过解除引用迭代器获得的值的类型。 对于输出迭代器,此类型应为void
。 - Distance: 类型为
difference_type
, 一种可用于标识迭代器之间距离的类型。即两个迭代器相减(若支持的话)的结果类型。 - Pointer: 类型为
pointer
,定义指向迭代类型的指针(T)。即指向 T 类型的指针。 - Reference: 类型为
reference
,定义迭代类型的引用(T)。即 T 的引用类型。
例如自定义一个继承自std::iterator
的迭代器:
#include <algorithm>
#include <iterator>
using namespace std;
template<long FROM, long TO>
class Range
{
public:
class Longiterator : public std::iterator<std::input_iterator_tag, //@ iterator_category
long, //@ value_type
long, //@ difference_type
const long*, //@ pointer
long> //@ reference
{
long num_ = FROM;
public:
explicit Longiterator(long num = 0) : num_(num) {}
Longiterator& operator++() { num_ = TO > FROM ? num_ + 1 : num_ - 1; return *this; }
Longiterator operator++(int) { Longiterator retval = *this; ++(*this); return retval; }
bool operator==(Longiterator other)const { return num_ == other.num_; }
bool operator!=(Longiterator other)const { return !(*this == other); }
reference operator*()const { return num_; }
};
Longiterator begin() { return Longiterator(FROM); }
Longiterator end() { return Longiterator(TO > FROM ? TO + 1 : TO - 1); }
};
int main()
{
auto range = Range<2, 10>();
auto iter = std::find(range.begin(),range.end(),8);
cout << *iter << endl;
//@ range_based for
for (const auto& elem : range)
cout << elem << " ";
cout << endl;
return 0;
}
std::iterator_traits
STL 中的算法与容器之间是通过迭代器架起的桥梁,算法需要知道迭代器的信息,以便采用最优算法。但是算法如何知道迭代器的详细信息呢,此时就需要一个中间层:iterator_traits
,它也是一个模板类,将 iteratoe
进行了包装,并使用模板局部特化技术:
//使用iterator提供的信息
template<typename Iterator>
struct iterator_traits
{
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_typep;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
//@ 局部特化,c++内置指针。
template<typename 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 c++内置指针。
template<typename T>
struct iterator_traits<const T *>
{
typedef random_access_iterator_tag iterator_category;
typedef T value_type; //@ 注意这里不是const T;如果是const T,算法拿到这个类型,用这个类型定义变量后,却无法改变其值,那就没有作用了,所以是T。
typedef ptrdiff_t difference_type;
typedef const T* pointer;
typedef const T& reference;
};
例如:list类的size方法。
size_type size() const {
size_type result = 0;
distance(begin(), end(), result);
return result;
//return distance(begin(), end());
}
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 {};
template <class InputIterator, class Distance>
inline void __distance(InputIterator first, InputIterator last, Distance& n,
input_iterator_tag)
{
while (first != last) { ++first; ++n; }
}
template <class RandomAccessIterator, class Distance>
inline void __distance(RandomAccessIterator first, RandomAccessIterator last,
Distance& n, random_access_iterator_tag)
{
n += last - first;
}
template <class Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&) {
typedef typename iterator_traits<Iterator>::iterator_category category; //@ 1
return category();
}
template <class InputIterator, class Distance>
inline void distance(InputIterator first, InputIterator last, Distance& n)
{
__distance(first, last, n, iterator_category(first));
}
代码1处,算法向 iterator_traits
要 iterator_category
的信息,如果 iterator
能提供,就使用 iterator
里的 iterator_category
,如果 iterator
不能提供,就使用 iterator_traits
里的 iterator_category
。得到iterator_category
后,就可以在编译阶段确定调用哪一个 __distance
方法了。