7
std::vector<int> ints;

// ... fill ints with random values

for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); )
{
    if(*it < 10)
    {
        *it = ints.back();
        ints.pop_back();
        continue;
    }
    it++;
}

pop_back()が呼び出されるitと無効になるため、このコードは機能しません。しかし、 のイテレータの無効化について話しているドキュメントは見つかりませんstd::vector::pop_back()

それについてのリンクはありますか?

4

11 に答える 11

15

を呼び出すとpop_back()、ベクター内の最後の要素が削除されるため、その要素への反復子は無効になります。このpop_back()呼び出しは、最後の要素の前のアイテムへの反復子を無効にしません。再割り当てのみが無効になります。Josuttis の「C++ 標準ライブラリ リファレンス」から:

要素を挿入または削除すると、次の要素を参照する参照、ポインター、および反復子が無効になります。挿入によって再割り当てが発生すると、すべての参照、反復子、およびポインターが無効になります。

于 2008-09-15T12:51:20.410 に答える
13

The Holy Standard からの直接の回答は次のとおりです。

23.2.4.2 ベクターは、オプションのシーケンス要件 (23.1.1) のほとんどを含む、コンテナーと可逆コンテナー (23.1 の 2 つの表に示されている) およびシーケンスのすべての要件を満たします。
23.1.1.12 表 68 式a.pop_back() 戻り値 typevoid 操作セマンティクスa.erase(--a.end()) containervector、list、deque

a.pop_back は a.erase(--a.end()) と同等であることに注意してください。消去に関するベクトルの詳細を見る:

23.2.4.3.3 - イテレータ消去(イテレータ位置) - 効果 -消去ポイント以降のすべてのイテレータと参照を無効にする

したがって、pop_back を呼び出すと、以前の最後の要素 (現在は存在しません) への反復子はすべて無効になります。

コードを見ると、問題は、最後の要素を削除してリストが空になった場合でも、それをインクリメントしてリストの最後から離れることです。

于 2008-09-15T13:25:29.547 に答える
5

(ここで入手可能なC++0x ワーキング ドラフトで使用されている番号付けスキームを使用します。

732 ページの表 94 は、pop_back (シーケンス コンテナーに存在する場合) には次の効果があることを示しています。

{ iterator tmp = a.end(); 
--tmp; 
a.erase(tmp); } 

23.1.1、ポイント 12 は次のように述べています。

特に指定されていない限り (明示的に、または他の関数に関して関数を定義することによって)、コンテナー メンバー関数を呼び出したり、コンテナーを引数としてライブラリー関数に渡したりしても、そのコンテナー内のオブジェクトの反復子を無効にしたり、その値を変更したりしてはなりません。 .

接頭辞を適用するように end() にアクセスすると、そのような効果はありませんが、ererase() は次のようになります。

23.2.6.4 (vector.erase() ポイント 4 について):

効果: 消去の時点またはそれ以降の反復子と参照を無効にします。

結論として、 pop_back() は、標準に従って、最後の要素へのイテレータのみを無効にします。

于 2008-09-15T13:29:12.410 に答える
3

以下は、SGI の STL ドキュメント ( http://www.sgi.com/tech/stl/Vector.html ) からの引用です。

[5] ベクターのイテレータは、メモリが再割り当てされると無効になります。さらに、ベクターの途中で要素を挿入または削除すると、挿入または削除ポイントに続く要素を指すすべての反復子が無効になります。したがって、reserve() を使用してベクターが使用するのと同じ量のメモリを事前に割り当て、すべての挿入と削除がベクターの最後にある場合、ベクターの反復子が無効化されるのを防ぐことができます。

pop_back は、最後の要素を指すイテレータと end() イテレータのみを無効にすることになると思います。コードが失敗するデータと、何が起こっているのかを判断するのに失敗する方法を実際に確認する必要があります。私が知る限り、コードは機能するはずです-そのようなコードの通常の問題は、@ mikhaildが指摘するように、イテレータの要素と ++ の削除が同じ反復で発生することです。ただし、このコードではそうではありません。pop_back が呼び出されたときに it++ は発生しません。

最後の要素を指していて、最後の要素が 10 未満の場合、何か問題が発生する可能性があります。現在、無効化された it と end() を比較しています。動作する可能性はありますが、保証はできません。

于 2008-09-15T13:13:53.100 に答える
1

イテレータは、ストレージの再割り当て時にのみ無効になります。Google はあなたの味方です。脚注 5 を参照してください。

あなたのコードは他の理由で機能していません。

于 2008-09-15T12:33:20.313 に答える
1

pop_back()最後の要素を指す反復子のみを無効にします。C++ 標準ライブラリ リファレンスから:

要素を挿入または削除すると、次の要素を参照する参照、ポインター、および反復子が無効になります。挿入によって再割り当てが発生すると、すべての参照、反復子、およびポインターが無効になります。

あなたの質問に答えるために、いいえ、すべてのイテレータを無効にするわけではありません。

ただし、コード例でitは、最後の要素を指しており、値が 10 未満の場合は無効になる可能性があります。その場合、Visual Studio デバッグ STL は反復子を無効としてマークし、それが end() と等しくないことをさらにチェックします。アサートを表示します。

反復子が純粋なポインターとして実装されている場合 (おそらくすべての非デバッグ STL ベクトルの場合と同様)、コードは問題なく動作するはずです。イテレータがポインタ以上の場合、コードは最後の要素を削除するこのケースを正しく処理しません。

于 2008-09-15T14:01:24.983 に答える
0

pop_back()は、ベクターの最後のアイテムを指している場合のみ無効にします。したがって、次のように、ベクトルの最後のintが10未満になると、コードは失敗します。

* it = ints.back(); // * itをすでに持っている値に設定します
ints.pop_back(); //イテレータを無効にします
continue; //ループして、無効なイテレータにアクセスします

于 2008-09-15T13:44:20.733 に答える
0

エラーは、「it」が vector の最後の要素を指し、この要素が 10 未満の場合、この最後の要素が削除されることです。そして今、「it」は ints.end() を指し、次の「it++」はポインタを ints.end()+1 に移動します。メモリー :)。

于 2008-09-15T12:47:52.710 に答える
0

「公式仕様」は C++ 標準です。C++03 のコピーにアクセスできない場合は、委員会の Web サイト ( http://www.open-std.org/jtc1/sc22/wg21/ ) から C++0x の最新のドラフトを入手できます。 docs/papers/2008/n2723.pdf

コンテナー要件の「操作上のセマンティクス」セクションでは、pop_back() が { iterator i = end(); と同等であることを指定しています。 - 私; 消去 (i); }。消去の [vector.modifiers] セクションには、「効果: 消去の時点以降の反復子と参照を無効にする」と記載されています。

直観引数が必要な場合、 pop_back は失敗しないため (標準コンテナーでの value_types の破棄は例外をスローすることが許可されていないため)、コピーや割り当てを行うことはできません (例外をスローできるため)。消去された要素への反復子と終了反復子は無効化されますが、残りは無効化されません。

于 2008-09-15T13:21:23.130 に答える
0

back 要素を削除された位置にスワップして戻す代わりに、erase の戻り値を使用することを検討することをお勧めします。シーケンスの場合、erase は、削除される要素の 1 つ先の要素を指すイテレータを返します。この方法では、元のアルゴリズムよりも多くのコピーが発生する可能性があることに注意してください。

for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); )
{
    if(*it < 10)
        it = ints.erase( it );
    else
        ++it;
}

std::remove_if代替ソリューションになる可能性もあります。

struct LessThanTen { bool operator()( int n ) { return n < 10; } };

ints.erase( std::remove_if( ints.begin(), ints.end(), LessThanTen() ), ints.end() );

std::remove_if(私の最初のアルゴリズムのように)安定しているので、これを行う最も効率的な方法ではないかもしれませんが、簡潔です。

于 2009-01-06T08:45:21.050 に答える
-1

ここ(cplusplus.com)の情報をチェックしてください:

最後の要素を削除する

ベクトルの最後の要素を削除し、ベクトルサイズを効果的に1つ減らし、すべてのイテレータとその要素への参照を無効にします。

于 2008-09-15T12:40:48.407 に答える