反向迭代器能否替代前向迭代器作为std::generate的参数?
我原本以为std::generate()只接受前向迭代器作为参数,但下面的代码却能正常编译运行,对此我感到困惑:
#include <iostream> #include <vector> #include <algorithm> using namespace std; void printer(int i) { cout << i << ", "; } struct Sequence { int start; Sequence(int start) :start(start) {} int operator() () { return start++ % 7; } }; int main() { vector<int> v1(4); generate(v1.rbegin(), v1.rend(), Sequence(10)); // Line I for_each(v1.begin(), v1.end(), printer); }
这段代码的输出是6, 5, 4, 3,。我想知道:反向迭代器是不是可以隐式转换为前向迭代器?
我曾猜测可能用到了迭代器的.base()方法,于是做了两次尝试:
- Line II:用
.base()转换反向迭代器后传入,代码编译通过但运行报错:generate(v1.rbegin().base(), v1.rend().base(), Sequence(10)); //Line II - Line III:调换了
.base()后的迭代器顺序,编译运行正常,但输出为3, 4, 5, 6,,和原代码逻辑不符:generate (v1.rend().base(), v1.rbegin().base(), Sequence(10)); // LINE III
解答
1. 反向迭代器为何能被std::generate接受
首先纠正一个误解:std::generate的迭代器要求是输入迭代器(C++11及以后标准),更早标准是前向迭代器,而反向迭代器本身就满足前向迭代器(甚至是随机访问迭代器,因为vector的反向迭代器属于随机访问类型)的所有要求,因此可以直接传入,不需要任何隐式转换。
2. Line I的运行逻辑
v1.rbegin()指向vector的最后一个元素,v1.rend()指向第一个元素的前一个位置。generate会从rbegin()开始,依次给每个迭代器指向的位置赋值:
- 第一次调用
Sequence(10):返回10%7=3,start变为11,赋值给v1[3] - 第二次:返回
11%7=4,start变为12,赋值给v1[2] - 第三次:返回
12%7=5,start变为13,赋值给v1[1] - 第四次:返回
13%7=6,start变为14,赋值给v1[0]
最后for_each从begin()到end()遍历输出,就是v1[0], v1[1], v1[2], v1[3],也就是6, 5, 4, 3,。
3. Line II报错的原因
反向迭代器的.base()方法返回的是对应的正向迭代器,但有个关键规则:反向迭代器it指向的元素,和it.base()指向的元素是相邻的——it.base()指向it所指元素的下一个位置。
v1.rbegin().base()等价于v1.end()(因为rbegin()指向最后一个元素,下一个位置就是尾后迭代器)v1.rend().base()等价于v1.begin()(rend()指向第一个元素的前一个位置,它的base就是第一个元素的迭代器)
所以Line II传入的范围是[v1.end(), v1.begin()),这是一个无效的反向范围,generate尝试访问这个范围的元素会触发越界,导致运行报错。
4. Line III输出不同的原因
Line III传入的范围是[v1.begin(), v1.end()),也就是正向遍历vector的所有元素。generate会依次给v1[0]到v1[3]赋值:
- 第一次赋值
3给v1[0],第二次4给v1[1],第三次5给v1[2],第四次6给v1[3]
最后遍历输出就是3, 4, 5, 6,,和Line I的反向填充逻辑完全相反,所以结果不同。
内容的提问来源于stack exchange,提问作者JBolt

