21

私が見つけたいくつかのSTLドキュメントによると、std :: listに要素を挿入または削除しても、イテレータは無効になりません。これは、リスト(frombegin()からend())をループしてから、push_frontを使用して要素を追加できることを意味します。

たとえば、次のコードでは、要素a、b、cを使用してリストを初期化し、それをループして要素のpush_frontを実行します。結果はcbaabcになるはずです。これは、まさに私が得たものです。

std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

for (std::list<std::string>::iterator itList = testList.begin(); itList != testList.end(); ++itList)
   testList.push_front(*itList);

for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
   std::cout << *itList << std::endl;

逆イテレータ(からrbegin()へのループrend())を使用してpush_backを使用すると、同様の動作、つまりabccbaの結果が期待されます。ただし、別の結果が得られます。

std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

for (std::list<std::string>::reverse_iterator itList = testList.rbegin(); itList != testList.rend(); ++itList)
   testList.push_back(*itList);

for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
   std::cout << *itList << std::endl;

結果はではありませんabccbaが、abcccbaです。そうです、追加されたcが1つあります。

最初のpush_backも、rbegin()で初期化されたイテレータの値を変更するようです。push_backの後、リストの3番目の要素(以前は最後の要素でした)ではなく、4番目の要素(現在は最後の要素)を指します。

Visual Studio 2010とGCCの両方でこれをテストしたところ、どちらも同じ結果を返しました。

これはエラーですか?または、私が気付いていない逆イテレータの奇妙な動作ですか?

4

3 に答える 3

16

標準では、イテレータと参照は挿入中も有効であるとされています。逆イテレータについては何も述べていません。:-)

reverse_iteratorによって返されるは、のrbegin()値を内部的に保持しますend()。この値の後は、push_back()明らかに以前と同じにはなりません。私は、標準がそれがどうあるべきかを述べているとは思いません。明らかな代替案には、リストの前の最後の要素が含まれるか、それが固定値(センチネルリンパ節など)の場合は最後にとどまります。


技術的な詳細:によって返される値は、有効ではないため、rend()前を指すことはできません。そのため、の値を含める必要があり、他のすべての逆イテレータをさらに1つシフトbegin()することが決定されました。これを補正し、とにかく正しい要素にアクセスします。rend()begin()operator*

24.5.1逆イテレータの最初の段落は次のように述べています。

クラステンプレートreverse_iteratorは、基になるイテレータによって定義されたシーケンスの最後からそのシーケンスの最初まで反復するイテレータアダプタです。逆イテレータとそれに対応するイテレータiの間の基本的な関係は、次のIDによって確立されます
&*(reverse_iterator(i)) == &*(i - 1)

于 2012-04-10T09:16:57.310 に答える
7

forこれを理解するには、ループをループとして再キャストすることから始めるのが最善だと思いwhileます。

typedef std::list<std::string> container;

container testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");

container::reverse_iterator itList = testList.rbegin(); 
while (itList != testList.rend()) {
    testList.push_back(*itList);
     ++itList;
}

それとともに、私たちはreverse_iterator一般的にどのように機能するかを理解する必要があります。具体的には、逆参照したときに取得した要素のreverse_iteratorの要素を実際に指します。コンテナの終了直後にイテレータを生成しますが、配列などの場合、コンテナの開始直前を指す定義された方法はありません。代わりにC++が行うのは、イテレータを終了直後から開始し、最初に進むことですが、間接参照すると、実際にポイントする直前の要素を取得します。end()

つまり、コードは実際には次のように機能します。

ここに画像の説明を入力してください

その後、Bを押してからAを押し戻すことで、期待どおりの結果が得られるため、最終的にABCCCBAになります。

于 2012-04-10T09:37:07.053 に答える
0

両方にイテレータを使用してみてください。試す:

std::list<std::string>::iterator i = testList.end(); 

--iで逆にします

于 2012-04-10T09:00:00.813 に答える