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()
が返ってきちゃいます。
おわり
イテレータ自体がそもそもわからないという人はというと...