C++ STL源码分析——iterator分类

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

【侯捷-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类型
vectorrandom_access_iterator_tag
listbidirectional_iterator_tag
forward_listforward_iterator_tag
dequerandom_access_iterator_tag
setbidirectional_iterator_tag
multisetbidirectional_iterator_tag
unordered_setbidirectional_iterator_tag
unordered_multisetbidirectional_iterator_tag
unordered_mapbidirectional_iterator_tag
unordered_multimapbidirectional_iterator_tag

]
可以看出,对于由哈希表结构组成 unordered_set, unordered_map 等,每个 bucket 上的链表采用的是一个双向链表

iterator_tag 的继承关系(回首页

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无法跳跃取值,只能前后依次遍历。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值