1. 算法与容器之间的关系
算法看不见容器, 对其一无所知, 因此它需要的信息都必须从iterator中获取;
所以iterator必须能够回答algorithm的所有提问, 才能把容器和算法联系起来.
2. iterator的种类
iterator的种类一共有5种, 分别是:
- input_iterator_tag
- output_iterator_tag
- forward_iterator_tag
- bidirectional_iterator_tag
- random_access_iterator_tag
继承关系如下图:
可以用下面代码进行实验测试:
#include <iostream> // std::cout
#include <iterator> // std::iterator_traits
#include <typeinfo> // typeid
#include <array>
#include <vector>
#include <list>
#include <forward_list>
#include <deque>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;
namespace sanjay
{
//以下5个方法都是重载, 虽然有些是继承关系, 但是也视为函数签名不一样, 同时只需要cout, 因此不需要拿到参数, 不需要在形参中声明变量
void display_category(random_access_iterator_tag)
{
cout << "random_access_iterator" << endl; //随机访问迭代器, 继承自bidirectional_iterator
}
void display_category(bidirectional_iterator_tag)
{
cout << "bidirectional_iterator" << endl; //双向迭代器, 继承于前向迭代器
}
void display_category(forward_iterator_tag)
{
cout << "forward_iterator" << endl; // 前向迭代器, 继承于input_iterator
}
void display_category(output_iterator_tag)
{
cout << "output_iterator" << endl;
}
void display_category(input_iterator_tag)
{
cout << "input_iterator" << endl;
}
template <typename I>
void display_category(I itr)
{
typename iterator_traits<I>::iterator_category cagy; //使用typename的原因, 使用typename 说明后面的是类型,
display_category(cagy); //调用前面5个函数
cout << "typeid(itr).name()= " << typeid(itr).name() << endl;
cout << endl;
}
void test_iterator_category()
{
cout << "\ntest_iterator_category().......... \n";
cout << endl;
display_category(array<int, 10>::iterator()); //random
display_category(vector<int>::iterator()); //random
display_category(list<int>::iterator()); //bidirectional
display_category(forward_list<int>::iterator()); //forward
display_category(deque<int>::iterator()); //random
display_category(set<int>::iterator());
display_category(map<int, int>::iterator());
display_category(multiset<int>::iterator());
display_category(multimap<int, int>::iterator());
display_category(unordered_set<int>::iterator()); //forward
display_category(unordered_map<int, int>::iterator());
display_category(unordered_multiset<int>::iterator());
display_category(unordered_multimap<int, int>::iterator());
display_category(istream_iterator<int>());
display_category(ostream_iterator<int>(cout, ""));
}
} // namespace sanjay
int main()
{
sanjay::test_iterator_category();
return 0;
}
输出结果如下:
test_iterator_category()..........
random_access_iterator
typeid(itr).name()= Pi
random_access_iterator
typeid(itr).name()= N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
bidirectional_iterator
typeid(itr).name()= St14_List_iteratorIiE
forward_iterator
typeid(itr).name()= St18_Fwd_list_iteratorIiE
random_access_iterator
typeid(itr).name()= St15_Deque_iteratorIiRiPiE
bidirectional_iterator
typeid(itr).name()= St23_Rb_tree_const_iteratorIiE
bidirectional_iterator
typeid(itr).name()= St17_Rb_tree_iteratorISt4pairIKiiEE
bidirectional_iterator
typeid(itr).name()= St23_Rb_tree_const_iteratorIiE
bidirectional_iterator
typeid(itr).name()= St17_Rb_tree_iteratorISt4pairIKiiEE
forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorIiLb1ELb0EEE
forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorISt4pairIKiiELb0ELb0EEE
forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorIiLb1ELb0EEE
forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorISt4pairIKiiELb0ELb0EEE
input_iterator
typeid(itr).name()= St16istream_iteratorIicSt11char_traitsIcElE
output_iterator
typeid(itr).name()= St16ostream_iteratorIicSt11char_traitsIcEE
需要注意的是:
unordered
类型的容器的迭代器类型是forward_iterator
类型的istream_iterator<int>()
和ostream_iterator<int>(cout, "")
都不是从标准容器实例化出来的对象, 但是也属于迭代器种类的两种, 后面会详细说明typename
放在迭代器萃取机
前面是因为还未能确定类型, 加了typename是给编译器说明这是个类型, 而不是变量, 具体参见这个博客
3. iterator_categore对算法的影响
如下面的distance
算法, 如果iterator是random类型的, 可以直接相减, 很快就可以得到结果; 而如果不是随机访问的迭代器类型, 则需要全部遍历一遍. 如果容器内有一千万个元素, 两者之间的效率就相差很多了.
下面的advance
算法同上:
3.1 新出现的type traits
标准库里面的copy
算法如下, 左边的代码是传入参数为InputIterator
泛化模板, 然后流程是这样的:
-
copy
函数传入的参数如果是const char*
或者是const wchar_t*
类型, 则进入特化流程, 调用底层库memmove
算法进行复制 -
如果不是上面情况, 进入泛化, 泛华之中又包含3个流程
- 如果为
T*
或者const T*
, 通过type traits
来获取赋值运算符是否重要(重要与否决定后续是否需要调用析构函数), 这个过程又称为强化 - 如果是
InputIterator
的话, 再分两个流程, 如果是random的话采用for(...;n < last-first;...)
, 这样速度更快; 否则用for(...; first!=last;...)
- 如果为
类似的例子还有destroy
函数:
还有__unique_copy
:
4. 算法源码中对iterator_category的"暗示"
比如标准库算法中的sort
, 采用的是快速排序, 而快排需要随机访问迭代器(因为快排需要交换不同位置的元素), 如下图所示, sort
算法的类模板中的模板参数命名为RandomAccessIterator
, 虽然换成其他名字也可以, 但是这是在告诉你需要传入随机访问迭代器.
很显然, list这种就不能用sort算法, 只能用其自身实现的sort.