C ++ベクトルを最後から最初まで反復する


96

ベクトルを最後から最初まで反復することは可能ですか?

for (vector<my_class>::iterator i = my_vector.end();
        i != my_vector.begin(); /* ?! */ ) {
}

または、それはそのようなものでのみ可能です:

for (int i = my_vector.size() - 1; i >= 0; --i) {
}

2
C ++ 11では、範囲ベースのforループをリバースアダプターで使用できます。ここを参照してください
MM

1
理論的には、32ビットマシンでは、2番目の解決策として、ベクトルサイズが2,147,483,647 + 1より大きい場合、オーバーフローします(vector :: size()は符号なし)。 32ビットマシンの現在のベクトル制限は1,073,741,823)です。
Stefan Rogin、2014

回答:


157

最善の方法は:

for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); 
        i != my_vector.rend(); ++i ) { 
} 

rbegin()/ rend()その目的のために特別に設計されました。(そうです、aをインクリメントreverse_interatorすると、後ろに移動します)

さて、理論的には、(begin()/ end()&を使用した--i)メソッドは機能しstd::vectorますが、反復子は双方向ですがend()、最後の要素ではないことに注意してください。これは最後の要素を超えたものなので、最初にデクリメントする必要があります。到達すると完了しますbegin()—ただし、処理を実行する必要があります。

vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
     --i;
    /*do stuff */

} 

更新:どうやらfor()ループをループに書き直すことにあまりにも積極的while()でした。(重要な部分は、それ--iが最初にあるということです。)


--iコンテナーが空の場合に大きな問題が発生することに気づきました... do - whileループに入る前にチェックすることは理にかなってい(my_vector.begin() != my_vector.end())ます。
a1ex07 2010

1
do-whileループだけでなくループを使用するのはなぜwhileですか?そうすれば、空のベクトルに対する特別なチェックは必要ありません。
jamesdlin 2010

auto読みやすくするために使用する回答を更新できますか?
LNJ

58

C ++ 11を使用している場合は、を利用できますauto

for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it)
{
}

28

クローズドオープンレンジを逆反復するための確立された「パターン」は、次のようになります。

// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
  // Process `*iterator`
}

または、必要に応じて

// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
  --iterator;
  // Process `*iterator`
}

このパターンは、たとえば、符号なしインデックスを使用して配列に逆インデックスを付ける場合に役立ちます。

int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
  array[i]; // <- process it
}

(このパターンに不慣れな人は、署名付きの使用を強く主張しますな人は、特に、符号なし整数型が何らかの理由で逆索引付けに「使用できない」と誤って信じているため、配列の索引付けに整数型をます)

「スライディングポインター」技術を使用して配列を反復するために使用できます。

// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
  *p; // <- process it
}

または、通常の(リバースではない)イテレータを使用して、ベクトルのリバース反復に使用できます

for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
  *i; // <- process it
}

cppreference.comは、end()で要素にアクセスすると「未定義の動作が発生する」と言っているので、ループは次の場所から開始する必要があると思います--end()
Thomas Schmid

@ThomasSchmidこれらのループは、にアクセスしようとすることはありませんend()。で始まるように見えend()ますが、最初のアクセスの前に必ずイテレータをデクリメントします。
AnT

これは、実行時にループすることができるため(テンプレートなし)、colbegin / rendよりもはるかに優れています auto a = vector<int>{0,1,2}; bool reversed = 0; auto it = (!reversed?a.begin():a.end()); auto end = (reversed?a.begin():a.end()); while(it != end) { if(reversed)--it; cout << *it << endl; if(!reversed)++it; }
colin

@colin Egads!それは醜い!。あなたはreversed 4回テストしています-ループ内のそれらの2つ。もちろん、ブール値のテストは非常に高速ですが、それでも、なぜ動作する必要がないのですか?特に、コードを読みにくくすることだけが目的のようです。2つの別々のループをどのように使用するのですか? if (reversed) for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) {doStuff(*it);} else for (auto it = my_vector.begin(); it != my_vector.end(); ++it) {doStuff(*it);}
James Curran

実際、あなたは私のポイントを逃しました。あなたはそれを2つに分割することは絶対に正しいですがif、私は上のテンプレートを取り除くことを望みましたdoStuff()。ただし、2つifのsを使用すると、最初の1つを逆にループすることで実行できます。
コリン

11

c ++ 20以降では、a std::ranges::reverse_viewと範囲ベースのforループを使用できます。

#include<ranges>
#include<vector>
#include<iostream>

using namespace std::ranges;

std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for(auto& i :  views::reverse(vec)) {
    std::cout << i << ",";
}

あるいは

for(auto& i :  vec | views::reverse)

残念ながら、執筆時点(2020年1月)では、主要なコンパイラーはrangeライブラリーを実装していませんが、Eric Nieblerのranges-v3に頼ることができます。

#include <iostream>
#include <vector>
#include "range/v3/all.hpp"

int main() {

    using namespace ranges;

    std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    for(auto& i :  views::reverse(vec)) {
        std::cout << i << ",";
    }

    return 0;
}

9

ユーザーrend() / rbegin()反復子:

for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)


5
template<class It>
std::reverse_iterator<It> reversed( It it ) {
  return std::reverse_iterator<It>(std::forward<It>(it));
}

次に:

for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
  std::cout << *rit;

あるいは、C ++ 14では次のようにします。

for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
  std::cout << *rit;

C ++ 03/11では、ほとんどの標準的なコンテナに.rbegin()and .rend()メソッドもあります。

最後に、backwards次のように範囲アダプターを作成できます。

namespace adl_aux {
  using std::begin; using std::end;
  template<class C>
  decltype( begin( std::declval<C>() ) ) adl_begin( C&& c ) {
    return begin(std::forward<C>(c));
  }
  template<class C>
  decltype( end( std::declval<C>() ) ) adl_end( C&& c ) {
    return end(std::forward<C>(c));
  }
}

template<class It>
struct simple_range {
  It b_, e_;
  simple_range():b_(),e_(){}
  It begin() const { return b_; }
  It end() const { return e_; }
  simple_range( It b, It e ):b_(b), e_(e) {}

  template<class OtherRange>
  simple_range( OtherRange&& o ):
    simple_range(adl_aux::adl_begin(o), adl_aux::adl_end(o))
  {}

  // explicit defaults:
  simple_range( simple_range const& o ) = default;
  simple_range( simple_range && o ) = default;
  simple_range& operator=( simple_range const& o ) = default;
  simple_range& operator=( simple_range && o ) = default;
};
template<class C>
simple_range< decltype( reversed( adl_aux::adl_begin( std::declval<C&>() ) ) ) >
backwards( C&& c ) {
  return { reversed( adl_aux::adl_end(c) ), reversed( adl_aux::adl_begin(c) ) };
}

そして今これを行うことができます:

for (auto&& x : backwards(ctnr))
  std::cout << x;

かなりきれいだと思います。



1

for eachコンストラクトの使用を許可し、C ++ 14 stdライブラリのみに依存する超シンプルな実装を次に示します。

namespace Details {

    // simple storage of a begin and end iterator
    template<class T>
    struct iterator_range
    {
        T beginning, ending;
        iterator_range(T beginning, T ending) : beginning(beginning), ending(ending) {}

        T begin() const { return beginning; }
        T end() const { return ending; }
    };

}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// usage:
//  for (auto e : backwards(collection))
template<class T>
auto backwards(T & collection)
{
    using namespace std;
    return Details::iterator_range(rbegin(collection), rend(collection));
}

これは、rbegin()とrend()を提供するもの、および静的配列で機能します。

std::vector<int> collection{ 5, 9, 15, 22 };
for (auto e : backwards(collection))
    ;

long values[] = { 3, 6, 9, 12 };
for (auto e : backwards(values))
    ;

0

Yakkの最後にある後方反復子が好きです-Adam Nevraumontの答えは、私が必要とするものに対して複雑に思われたので、これを書きました。

template <class T>
class backwards {
    T& _obj;
public:
    backwards(T &obj) : _obj(obj) {}
    auto begin() {return _obj.rbegin();}
    auto end() {return _obj.rend();}
};

私はこのような通常のイテレータをとることができます:

for (auto &elem : vec) {
    // ... my useful code
}

逆に繰り返すには、これを次のように変更します。

for (auto &elem : backwards(vec)) {
    // ... my useful code
}

0

The Boost Libraryを使用できる場合は、次のものを含めることでreverse範囲アダプターを提供するBoost.Rangeがあります。

#include <boost/range/adaptor/reversed.hpp>

次に、C ++ 11のrange- forloopと組み合わせて、次のように記述するだけです。

for (auto& elem: boost::adaptors::reverse(my_vector)) {
   // ...
}

このコードはイテレーターペアを使用するコードよりも短いため、注意すべき詳細が少ないため、コードが読みやすくなり、エラーが発生しにくくなる可能性があります。


-1

このコードを使用

//print the vector element in reverse order by normal iterator.
cout <<"print the vector element in reverse order by normal iterator." <<endl;
vector<string>::iterator iter=vec.end();
--iter;
while (iter != vec.begin())
{
    cout << *iter  << " "; 
    --iter;
}

-2

私はエイリアンマーズの新しい構文を何も導入したくなく、単に既存のプリミティブを基にしたいので、以下のスニペットが機能するようです:

#include <vector>
#include <iostream>

int main (int argc,char *argv[])
{
    std::vector<int> arr{1,2,3,4,5};
    std::vector<int>::iterator it;

    for (it = arr.begin(); it != arr.end(); it++) {
        std::cout << *it << " ";
    }

    std::cout << "\n************\n";

    for (it = arr.end() - 1; it != arr.begin()-1;it--) {
        std::cout << *it << " ";
    }

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