ベクター内の要素のErasing()が機能しない


10

ベクターを持っています。その中の最後の3つの要素を削除する必要があります。このロジックについて説明しました。プログラムがクラッシュします。何が間違いでしょうか?

vector<float>::iterator d = X.end();
    for (size_t i = 1; i < 3; i++) {
        if (i == 1) X.erase(d);
        else X.erase(d - i);
    }

ここのキラーdは実際には存在しません。これは、の終わりを見つけるためにのみ使用できる、過去の終わりのカナリア値vectorです。削除することはできません。次に、イテレータを消去するとすぐに消えます。その後、を含め、何でも安全に使用することはできませんd - i
user4581301

回答:


9

ベクターに少なくとも3つのアイテムがある場合、最後の3つのアイテムを削除するのは簡単です。pop_backを 3回使用するだけです。

#include <vector>
#include <iostream>

int main() 
{
    std::vector<float> v = { 1, 2, 3, 4, 5 };
    for (int i = 0; i < 3 && !v.empty(); ++i)
       v.pop_back();

    for ( const auto &item : v ) std::cout << item << ' ';
        std::cout << '\n';
}

出力:

1 2

11

イテレータを1パラメータのオーバーロードに渡すことは未定義の動作です。そうでなかったとしても、指定された要素の「以降」にあるイテレータを無効化し、最初のループ反復後に無効化します。end()erase()erase()d

std::vectorerase()削除する要素の範囲を受け入れる2パラメータのオーバーロードがあります。手動ループはまったく必要ありません。

if (X.size() >= 3)
    X.erase(X.end()-3, X.end());

ライブデモ


3

まず、X.end()イテレータをベクトルの最後の要素に戻さず、ベクトルの最後の要素を過ぎた要素にイテレータを戻します。これは、ベクトルが実際には所有していない要素です。そのため、X.erase(d)プログラムのクラッシュでそれを消去します。

代わりに、ベクターに少なくとも3つの要素が含まれている場合、次の操作を実行できます。

X.erase( X.end() - 3, X.end() );

代わりに、最後から3番目の要素に移動し、その後に到達するまですべての要素を消去しX.end()ます。

EDIT:だけを明確にするには、X.end()あるLegacyRandomAccessIterator有効持つように指定されている-別の返す操作LegacyRandomAccessIteratorを


2

end()from cppreferenceの定義は次のとおりです。

ベクトルコンテナー内の過去の最後の要素を参照する反復子を返します。

そして少し下:

それはいかなる要素も指さないので、逆参照されません。

つまり、ベクターにはend()が指す要素がありません。erase()メソッドを介してその非要素を逆参照することにより、ベクターに属していないメモリを変更している可能性があります。したがって、そこから醜いことが起こります。

間隔を[低、高)として記述するのは通常のC ++規則であり、「低」値は間隔に含まれ、「高」値は間隔から除外されます。


2

あなたは使うことができますreverse_iterator

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<float> X = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};

    // start the iterator at the last element
    vector<float>::reverse_iterator rit = X.rbegin();

    // repeat 3 times
    for(size_t i = 0; i < 3; i++)
    {
        rit++;
        X.erase(rit.base());
    }

    // display all elements in vector X
    for(float &e: X)
        cout << e << '\n';

    return 0;
}

言及すべきことがいくつかあります。

  • reverse_iterator ritの最後の要素から始まりvector Xます。この位置をと呼びrbeginます。
  • erase使用するにはクラシックiteratorが必要です。これはrit、を呼び出すことで取得できbaseます。しかし、その新しいイテレータはrit、順方向から次の要素を指します。
  • そのため、rit電話をかける前にbaseerase

また、について詳しく知りたい場合reverse_iteratorは、この回答にアクセスすることをお勧めします


2

質問のコメント(現在は削除済み)には、「イテレータの演算子はありません」とありました。ただし、次のコードのコンパイルと作品の両方でMSVCclang-cl、標準セットのいずれかにC++17C++14

#include <iostream>
#include <vector>

int main()
{
    std::vector<float> X{ 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f };
    for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
    std::vector<float>::iterator d = X.end();
    X.erase(d - 3, d);  // This strongly suggest that there IS a "-" operator for a vector iterator!
    for (auto f : X) std::cout << f << ' '; std::cout << std::endl;
    return 0;
}

の定義operator-は次のとおりです(<vector>ヘッダー内):

    _NODISCARD _Vector_iterator operator-(const difference_type _Off) const {
        _Vector_iterator _Tmp = *this;
        return _Tmp -= _Off;
    }

ただし、私は確かにC ++言語の弁護士ではありません。これは、これらの「危険な」Microsoft拡張機能の1つである可能性があります。これが他のプラットフォーム/コンパイラで動作するかどうか知りたいです。


2
ベクトルのイテレータはランダムアクセスであり-、これらのタイプのイテレータに対して定義されているため、これは有効だと思います。
PaulMcKenzie

@PaulMcKenzie確かに-clangスタティックアナライザー(標準ではかなり厳密になる可能性があります)は警告を出しませんでした。
エイドリアンモル

1
operator-イテレータが定義されていなくても、std::advance()またはstd::prev()代わりに使用できます。
レミールボー

1

この文

    if (i == 1) X.erase(d);

未定義の動作があります。

そして、このステートメントは最後の要素の前の要素のみを削除しようとします

    else X.erase(d - i);

ループが2回しかないため

for (size_t i = 1; i < 3; i++) {

次のようなものが必要です。

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

int main() 
{
    std::vector<float> v = { 1, 2, 3, 4, 5 };

    auto n = std::min<decltype( v.size() )>( v.size(), 3 ); 
    if ( n ) v.erase( std::prev( std::end( v ), n ), std::end( v ) );

    for ( const auto &item : v ) std::cout << item << ' ';
    std::cout << '\n';

    return 0;
}

プログラム出力は

1 2 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.