std::sort(a.rbegin(), a.rend());
みたいなやつです。
便宜上 std::vector のようなものを考えますが、std::set とかについても同様です(random-access モノは除く)。
普通のイテレータ
まず普通のイテレータに触れましょう。
[0][1][2]...[n-1][-] ^ a.begin() ^ a.end()
のように要素を指していると見ることもできますが、次のように境界を指しているとも見られます。
|0|1|2|...|n-1| ^ a.begin() ^ a.end()
普通のイテレータで、* でアクセスすると、その境界の次にある要素を返します。
v *it ...|i|... ^ it
++it をすると、その次の境界を指すようになります。
v ++it ...|i|i+1|... ^ it
++ で進む方向は、begin 側から end 側です。
逆向きイテレータ
std::reverse_iterator というクラスがあって、普通のイテレータを基にしつつ逆向きに動かすようにしたものです。
a.rbegin() で返ってくるものは std::reverse_iterator(a.end()) です。
v a.rend() v a.rbegin() |0|1|2|...|n-1| ^ a.begin() ^ a.end()
で、逆向きイテレータの進む向きは(通常の意味での)end から begin に進む向きです。
rbegin から rend に進む向きです。
逆向きイテレータの ++ では、内部で持っているイテレータに対して -- をしています。
reverse_iterator& operator ++() {
--it;
return *this;
}
みたいな感じです。
* では、その境界の次(進む向き)にある要素を取り出します。
v *a.rbegin()
|0|1|...|n-1|...
^ a.rbegin()
reference operator *() const {
auto tmp = it;
--tmp;
return *tmp;
}
のような感じです。
という感じで、++ や * などがうまく定義されているので、逆向きイテレータを使って区間への操作ができます。
std::sort(a.rbegin(), a.rend());
とすると降順ソートされますが、やっていることに忠実に言えば、「後ろから見て昇順ソートする」です。
STL の関数に渡されてしまえば、逆向きだろうが関係ないので、うまくいってくれます。
* でアクセスするたびに -- が余分に呼ばれるので、std::greater<>() を渡す方法よりもほんの少し遅い傾向にありそうです?(あるいはアクセス順とかも関係ある?)
一方で、*a.rbegin() で最後の要素を取得できることから類推して、最後の要素を消したくて次のように書く人がいそうです。
a.erase(a.rbegin()); // ?
これは、できなくて、a.erase の引数は std::reverse_iterator ではなく a 自身のイテレータである必要があるためです。
a.erase(std::prev(a.end())); // a.erase(a.end()-1); // valid if random-access iterator
がんばりましょう。
a.rbegin().base() で返ってくるイテレータは所望のイテレータではないので注意です。
a.rbegin() は std::reverse_iterator(a.end()) なので、a.end() が返ってきちゃいます。
おわり
イテレータ自体がそもそもわからないという人はというと...