0

おもちゃのプログラム (post.cpp) を考えてみましょう:

#include <iostream>
#include <vector>

using namespace std;
int main() {
        vector<int > a;
        int i;
        for(i=0;i<10;i++)
                a.push_back(i);
        auto it=a.rbegin();
        while(it!=a.rend()) {
                if ((*it % 2)==0) {
                                cout << "about to erase "<<*it<<endl;
                                a.erase((it++).base());
                }
                else {
                        ++it;
                }
        }
        for(auto it2=a.begin(); it2 != a.end(); it2++) {
                cout << *it2 << endl;
        }
        return 0;
}

私がやろうとしているのは、偶数性をテストしてから(it++)、現在のイテレータを返し、イテレータを進める必要があるため、現在の番号を削除することです。これは私が出力として得るものです:

$ ./post 
about to erase 8
about to erase 6
about to erase 4
about to erase 2
about to erase 0
0
2
4
6
8

ただし、行a.erase((it++).base());をに変更するa.erase((++it).base());と、正しい動作が得られます。どうしてこれなの?

有用な説明: ではbase()reverse_iterators を使用できないため、使用していerase()ます。ベクトルを逆にして消去したいアプリケーションがあります。

4

2 に答える 2

3

逆イテレータのbase()は1だけオフセットされます。したがってrbegin().base() == end()rend().base() == begin()

これは、この逆ループの一般化に他なりません。

for (size_t i = 0; i != N; ++i)
{
    mutate(array[N - i - 1]);
}   //                 ^^^

ループは逆の順序で配列をトラバース- 1し、「イテレータ」にがどのように必要かを確認します。


更新:次に、逆イテレーターとは何かを調べてみましょう。これは、双方向イテレーターの単なるラッパーです。逆イテレータが呼び出されたとしitます; 次に、通常のイテレータメンバーがありit.base()ます。たとえば、v.rbegin().base() == v.end()。あなたが言うとき、それはただ(概念的に)++it呼び出すだけです。本当の魔法は逆参照操作です。これにより、基になるイテレータの前に1つの要素--it.base()が与えられます。

*it == *(it.base() - 1)

これは、配列の後ろからi 番目の要素が1つオフセットされていることを示した算術とまったく同じです。これは、逆イテレータを形成するために双方向イテレータが必要な理由も示しています。array[N - i - 1]

これで、ノードベースのコンテナなど、イテレータを無効にしないコンテナから逆イテレータを介して消去する方法が明確になりました。

if (meets_condition(*it))   // this examines *(it.base() - 1)!
{
     auto b = it.base();
     container.erase(it.base() - 1);
     it = std::reverse_iterator(b);
}

これには、ノードベースのコンテナのように、消去によって消去以外のイテレータが無効にならないことが必要であることに注意してください。このようにベクトルから消去するのはさらに難しいでしょう。ベクトルの場合、消去すると、消去を通過したすべてのイテレータが(順方向に)無効になるため、erase関数の戻り値を使用する必要があります。

if (meets_condition(*ut))   // again, examine *(it.base() - 1)
{
    it = std::reverse_iterator(container.erase(it.base() - 1));
}

写真では(要素「5」を削除しています):

+---+---+---+---+---+---+---+
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |     =:   v
+---+---+---+---+---+---+---+
              ^   ^
              |   |
|             |   +--- it.base()
|             |
|             +--- *it == *(it.base() - 1)
|
V

+---+---+---+---+---+---+
| 2 | 3 | 4 | 6 | 7 | 8 |
+---+---+---+---+---+---+
          ^   ^
          |   |
          |   +--- result of v.erase(it.base() - 1)
          |
          +--- *(std::reverse_iterator(v.erase(it.base() - 1)))
于 2012-09-25T16:55:50.547 に答える
1

あなたの質問への答えではなく、問題の解決策です:現在行っていることよりも効率的な消去削除イディオムを使用してください:

bool even( int value ) { return !(value%2); }
std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 }; // Assuming C++11 or build it otherwise
v.erase( std::remove_if( v.begin(), v.end(), even ),
         v.end() );

効率の違いはremove_if、コンテナーに残っている値のみを最終的な場所に1 回コピーするのに対し、アルゴリズムは一部の要素を複数回コピーする可能性があることです。特に、9 は 4 回、7 は 3 回というようにコピーされます。

于 2012-09-25T17:05:07.883 に答える