头文件<iterator>中定义了四种迭代器:
- 插入迭代器(insert iterator):这些迭代器被绑定到一个容器上,可向容器插入元素。
- 流迭代器(stream iterator):这些迭代器被绑定到输入或输出流上,可用来遍历关联的IO流。
- 反向迭代器(reverse iterator):这些迭代器向后而不是向前移动。除了forward_list之外的标准库容器都有反向迭代器。
- 移动迭代器(move iterator):这些专用的迭代器不是拷贝其中的元素,而是移动它们。
插入器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素。当我们通过一个插入迭代器进行赋值时,该迭代器调用容器操作来向给定容器的指定位置插入一个元素。it = t; 即在it指定的当前位置插入值t。假定c是it绑定的容器,依赖于插入迭代器的不同种类,此赋值会分别调用c.push_back(t), c.push_front(t)或c.insert(t, p), 其中p为传递给inserter的迭代器位置。
与上面相对应,插入器有三种类型,back_inserter, front_inserter, inserter,差异在于元素插入的位置。
list<int> lst = {1, 2, 3, 4};
list<int> lst2, lst3;
//拷贝完成之后,lst2包含4 3 2 1
copy(lst.cbegin(), lst.cend(), front_inserter(lst2) );
//拷贝完成之后,lst3包含1 2 3 4
copy(lst.cbegin(), lst.cend(), inserter(lst3, lst3.begin() ) );
iostream迭代器,istream_iterator读取输入流,ostream_iterator向一个输入流中写数据。这些迭代器,将他们对应的流当做一个特定类型的元素序列来处理。通过使用流迭代器,我们可以用泛型算法从流对象读取数据以及向其写入数据。
istream_iterator的一些操作
istream_iterator<int> int_it(cin);
istream_iterator<int> int_eof;
ifstream in("afile");
istream_iterator<string> str_it(in);
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
while (in_iter != eof)
//后置递增运算读取流,返回迭代器的旧值
//解引用迭代器,获得从流读取的前一个值
vec.push_back(*in_iter++);
//上面的一小段程序可以重写为如下形式:
istream_iterator<int> in_iter(cin), eof;
vector<int> vec(in_iter, eof);
//使用算法操作流迭代器
istream_iterator<int> in(cin), eof;
cout << accumulate(in, eof, 0) << endl;
istream_iterator允许懒惰求值,也即是当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器立即从流读取数据。具体实现可以推迟从流中读取数据,直到我们使用迭代器时才真正读取。标准库中的实现所保证的是,在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成了。对于大多数程序来说,立即读取还是推迟读取没什么差别。但是,如果我们创建了一个istream_iterator,没有使用就销毁了,或者我们正在从两个不同的对象同步读取同一个流,那么何时读取可能就很重要了。
ostream_iterator创建时,可以提供(可选的)第二参数,它是一个字符串,在输出每个元素后都会打印此字符串。此字符串必须是一个C风格字符串(即,一个字符串字面常量或者一个指向以空字符结尾的字符数组的指针)。必须将ostream_iterator绑定到一个指定的流,不允许空的或表示尾后位置的ostream_iterator。
//用ostream_iterator来输出值的序列
ostream_iterator<int> out_iter(cout, " ");
for(auto e : vec)
*out_iter++ = e;
cout << endl;
//下面的程序与上面的等价
for(auto e : vec)
out_iter = e;
cout << endl;
运算符*和++实际上对ostream_iterator对象不做任何事情,因此忽略它们对我们的程序没有任何影响。但是,推荐第一种形式。在这种写法中,流迭代器的使用与其他迭代器的使用保持一致。如果想将此循环改为操作其他迭代器类型,修改起来非常容易。而且,对于读者来说,此循环的行为也更加清晰。
可以通过调用copy来打印vec中的元素,这比编写循环更为简单:
copy(vec.begin(), vec.end(), out_iter);
cout << endl;
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。对于反向迭代器,递增(以及递减)操作的含义会颠倒过来。递增一个反向迭代器(++it)会移动到前一个元素;递减一个元素(--it)会移动到下一个元素。
反向迭代器与普通迭代器的关系,rcomma如果是一个反向迭代器,那么rcomma.base()就是一个正向迭代器。
新标准库中定义了一种移动迭代器(move iterator)适配器,一个移动迭代器通过改变给定迭代器的解引用运算符的行为来适配此迭代器。一般来说,一个迭代器的解引用运算符返回一个指向元素的左值。与其他迭代器不同,移动迭代器的解引用运算符生成一个右值引用。
我们通过调用标准库的make_move_iterator函数将一个普通迭代器转换为一个移动迭代器。此函数接受一个迭代器参数,返回一个移动迭代器。
原迭代器的所有其他操作在移动迭代器中都照常工作。由于移动迭代器支持正常的迭代器操作,我们可以将一对移动迭代器传递给算法。
值得注意的是,标准库不保证哪些算法适用移动迭代器,哪些不适用。由于移动一个对象可能销毁原对象,因此你只有在确信算法在为一个元素赋值或将其传递给一个用户定义的函数后不再访问它时,才能将移动迭代器传递给算法。