【侯捷-SL体系结构内核分析-iterator】
目录:
iterator需要遵循的原则
iterator的‘萃取机’
iterator的分类
iterator_tag 的继承关系
iterator_tag对 algorithm的影响
iterator需要遵循的原则
先来看一段代码,目的是移动 iterator 若干距离:
template<class _Iter>
using _Iter_cat_t = typename iterator_traits<_Iter>::iterator_category;
template<class _InIt,
class _Diff>
_CONSTEXPR17 void advance(_InIt& _Where, _Diff _Off)
{ // increment iterator by offset, arbitrary iterators
// we remove_const_t before _Iter_cat_t for better diagnostics if the user passes an iterator that is const
_Advance1(_Where, _Off, _Iter_cat_t<remove_const_t<_InIt>>());
}
template<class _RanIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_RanIt& _Where, _Diff _Off, random_access_iterator_tag)
{ // increment iterator by offset, random-access iterators
_Where += _Off;
}
template<class _BidIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag)
{ // increment iterator by offset, bidirectional iterators
for (; 0 < _Off; --_Off)
{
++_Where;
}
// the following warning is triggered if _Diff is unsigned
#pragma warning(suppress: 6294) // Ill-defined for-loop: initial condition does not satisfy test.
// Loop body not executed.
for (; _Off < 0; ++_Off)
{
--_Where;
}
}
template<class _InIt,
class _Diff>
_CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag)
{ // increment iterator by offset, input iterators
_STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator");
for (; 0 < _Off; --_Off)
{
++_Where;
}
}
iterator 作为 container 和 algorithm 之间的桥梁,必须要有能力回答出 algorithm 所提出的问题,如上面的
template<class _Iter>
using _Iter_cat_t = typename iterator_traits<_Iter>::iterator_category;
就是在询问 iterator,“请问你是什么类型的 iterator 呢?”,然后 iterator 根据自身的类型做出相应的回答,最后算法根据 iterator 的回答重载不同的函数,进行不同的操作,提高算法的效率。这样的询问在 C++标准库中设计出5种:
- iterator_category,iterator 的类型
- difference_type,头尾 iterator 之间距离的类型
- value_type,容器值类型
- reference,容器值引用
- pointer,容器值指针
这就是 iterator 需要遵循的设计原则,为 algorithm 提供这5种参数。 其中,reference 和 pointer 尚未在C++标准库中使用。
以 vector 的 iterator 为例,它有关体现这种设计原则的代码如下,均以类型别名的形式:
template<class _Myvec>
class _Vector_iterator
: public _Vector_const_iterator<_Myvec>
{ // iterator for mutable vector
public:
using _Mybase = _Vector_const_iterator<_Myvec>;
using iterator_category = random_access_iterator_tag;
using value_type = typename _Myvec::value_type;
using difference_type = typename _Myvec::difference_type;
using pointer = typename _Myvec::pointer;
using reference = value_type&;
...
}
还有一个问题没有解决,上面的 iterator_traits 又是什么呢?iterator 是如何对 algorithm 的提问做出回答呢?
iterator的‘萃取机’ (回首页)
iterator_traits 可以称为 iterator 的萃取机,将 iterator 作为模板参数传递给 iterator_traits,它必须要有能力分辨出它所获得的 iterator 是 class iterator T 还是 native pointer to T。利用偏特化(partial specialization)就可以达到目的。
template<class _Iter>
struct iterator_traits
: _Iterator_traits_base<_Iter>
{ // get traits from iterator _Iter, if possible
};
template<class _Iter>
struct _Iterator_traits_base<_Iter, void_t<
typename _Iter::iterator_category,
typename _Iter::value_type,
typename _Iter::difference_type,
typename _Iter::pointer,
typename _Iter::reference
>>
{ // defined if _Iter::* types exist
using iterator_category = typename _Iter::iterator_category;
using value_type = typename _Iter::value_type;
using difference_type = typename _Iter::difference_type;
using pointer = typename _Iter::pointer;
using reference = typename _Iter::reference;
};
//偏特化,如果传入的是指针
template<class _Ty>
struct iterator_traits<_Ty *>
: _Iterator_traits_pointer_base<_Ty>
{ // get traits from pointer, if possible
};
template<class _Ty,
bool = is_object_v<_Ty>>
struct _Iterator_traits_pointer_base
{ // iterator properties for pointers to object
using iterator_category = random_access_iterator_tag;
using value_type = remove_cv_t<_Ty>;
using difference_type = ptrdiff_t;
using pointer = _Ty *;
using reference = _Ty&;
};
iterator的分类(回首页)
iterator分为以下五种类型:
- random_access_iterator_tag,可跨任意距离取值
- bidirectional_iterator_tag,可前进或后退
- forward_iterator_tag
- input_iterator_tag
- output_iterator_tag
利用下面的代码对各种容器的 iterator 进行测试:
void DisplayIteratorCategory(random_access_iterator_tag)
{
cout << "random_access_iterator_tag" << endl;
}
void DisplayIteratorCategory(bidirectional_iterator_tag)
{
cout << "bidirectional_iterator_tag" << endl;
}
void DisplayIteratorCategory(forward_iterator_tag)
{
cout << "forward_iterator_tag" << endl;
}
void DisplayIteratorCategory(input_iterator_tag)
{
cout << "input_iterator_tag" << endl;
}
void DisplayIteratorCategory(output_iterator_tag)
{
cout << "output_iterator_tag" << endl;
}
template <typename T>
void GetIteratorType(T it)
{
typename iterator_traits<T>::iterator_category it_cty;
DisplayIteratorCategory(it_cty);
}
int main()
{
GetIteratorType(vector<int>::iterator());
GetIteratorType(list<int>::iterator());
GetIteratorType(forward_list<int>::iterator());
GetIteratorType(deque<int>::iterator());
GetIteratorType(set<int>::iterator());
GetIteratorType(multiset<int>::iterator());
GetIteratorType(unordered_set<int>::iterator());
GetIteratorType(unordered_multiset<int>::iterator());
GetIteratorType(unordered_map<int, string>::iterator());
GetIteratorType(unordered_multimap<int, string>::iterator());
GetIteratorType(istreambuf_iterator<int>());
GetIteratorType(ostreambuf_iterator<char>(cout));
return 0;
}
得到的结果如下:

可以看出,各个容器的 iterator 的类型如下表:
| 容器 | iterator类型 |
|---|---|
| vector | random_access_iterator_tag |
| list | bidirectional_iterator_tag |
| forward_list | forward_iterator_tag |
| deque | random_access_iterator_tag |
| set | bidirectional_iterator_tag |
| multiset | bidirectional_iterator_tag |
| unordered_set | bidirectional_iterator_tag |
| unordered_multiset | bidirectional_iterator_tag |
| unordered_map | bidirectional_iterator_tag |
| unordered_multimap | bidirectional_iterator_tag |
]
可以看出,对于由哈希表结构组成 unordered_set, unordered_map 等,每个 bucket 上的链表采用的是一个双向链表。
iterator_tag 的继承关系(回首页)
iterator_tag的继承关系如下图所示:

iterator_tag对 algorithm的影响(回首页)
以算法 distance为例,该算法旨在求两个 iterator 之间的距离。算法源代码如下:
template<class _InIt>
_NODISCARD _CONSTEXPR17 _Iter_diff_t<_InIt> distance(_InIt _First, _InIt _Last)
{ // return distance between iterators
return (_Distance1(_First, _Last, _Iter_cat_t<_InIt>()));
}
先来看看 _Iter_cat_t 的定义:
template<class _Iter>
using _Iter_cat_t = typename iterator_traits<_Iter>::iterator_category;
可以看出,_Iter_cat_t 是 iterator_traits<_Iter>::iterator_category 的类型别名,即通过 iterator_traits 萃取出参数 _Iter 的 iterator_category 参数。
_Distance1 有两个重载函数,如下:
template<class _InIt>
_CONSTEXPR17 _Iter_diff_t<_InIt> _Distance1(_InIt _First, _InIt _Last, input_iterator_tag)
{ // return distance between iterators; input
_Iter_diff_t<_InIt> _Off = 0;
for (; _First != _Last; ++_First)
{
++_Off;
}
return (_Off);
}
template<class _RanIt>
_CONSTEXPR17 _Iter_diff_t<_RanIt> _Distance1(_RanIt _First, _RanIt _Last, random_access_iterator_tag)
{ // return distance between iterators; random-access
return (_Last - _First);
}
- 当 iterator_category 是 random_access_iterator_tag时,代表存储的容器为连续内存(至少对外表现的是这样),可以在O(1)时间内前后跳跃取值,重载选取下面的_Distance1函数;
- 如果 iterator_category 是input_iterator_tag时 (包括它的子类bidirection_iterator_tag, forward_iterator_tag),iterator无法跳跃取值,只能前后依次遍历。

本文探讨了C++ STL中iterator遵循的原则,包括iterator_category、difference_type等五种参数设计,以及iterator的分类,如random_access_iterator_tag、bidirectional_iterator_tag等。还介绍了iterator_tag的继承关系及其对algorithm如distance的影响,展示了不同容器iterator类型的测试结果。

431

被折叠的 条评论
为什么被折叠?



