488

C++でベクトルを反復処理する正しい方法は何ですか?

次の 2 つのコード フラグメントを検討してください。これは正常に動作します。

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

そしてこれ:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

を生成しwarning: comparison between signed and unsigned integer expressionsます。

私は C++ の世界に慣れていないので、unsigned変数は少し恐ろしく見えますunsigned。正しく使用しないと変数が危険になる可能性があることはわかっています。これは正しいですか?

4

18 に答える 18

854

逆方向の反復については、この回答を参照してください。

前方への反復はほとんど同じです。イテレータを変更するだけで、デクリメントをインクリメントでスワップできます。イテレータを優先する必要があります。std::size_tインデックス変数型として使用するように言う人もいます。ただし、それは移植性がありません。常にコンテナの typedef を使用してください (前方反復の場合は変換のみで回避できますが、後方反復の場合は、 の typedef よりも幅が広い場合size_typeに、実際には完全にうまくいかない可能性があります) 。 :std::size_tstd::size_tsize_type


std::vector の使用

イテレータの使用

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

重要なのは、定義がわからないイテレータには、常にプレフィックスのインクリメント形式を使用することです。これにより、コードが可能な限り汎用的に実行されるようになります。

範囲 C++11 の使用

for(auto const& value: a) {
     /* std::cout << value; ... */

インデックスの使用

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

配列の使用

イテレータの使用

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

範囲 C++11 の使用

for(auto const& value: a) {
     /* std::cout << value; ... */

インデックスの使用

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

sizeofただし、アプローチがどのような問題につながる可能性があるかについては、後方反復の回答を読んでください。

于 2009-01-03T17:17:48.927 に答える
178

4年が経ち、Googleは私にこの答えをくれました。標準C++11(別名C ++ 0x)には、実際にはこれを行うための新しい快適な方法があります(下位互換性を壊すことを犠牲にして):新しいautoキーワード。使用するイテレータのタイプが(コンパイラにとって)明らかな場合に、使用するイテレータのタイプを明示的に指定する(ベクトルタイプを再度繰り返す)必要がなくなります。あなたであることで、あなたはこのようなことをすることができますvvector

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C ++ 11はさらに進んで、ベクトルなどのコレクションを反復処理するための特別な構文を提供します。それは常に同じものを書く必要性を取り除きます:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

動作中のプログラムで表示するには、ファイルを作成しますauto.cpp

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

これを書いている時点で、これをg ++でコンパイルするときは、通常、追加のフラグを指定して、新しい標準で動作するように設定する必要があります。

g++ -std=c++0x -o auto auto.cpp

これで、例を実行できます。

$ ./auto
17
12
23
42

コンパイルと実行の手順はLinux上のgnuc++コンパイラに固有であり、プログラムはプラットフォーム(およびコンパイラ)に依存しないことに注意してください。

于 2013-03-05T17:10:17.500 に答える
43

あなたの例の特定のケースでは、STLアルゴリズムを使用してこれを達成します。

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

より一般的ですが、それでもかなり単純なケースでは、次のようにします。

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );
于 2009-01-03T18:03:29.803 に答える
38

Johannes Schaubの答えについて:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

一部のコンパイラでは機能する可能性がありますが、gcc では機能しません。ここでの問題は、std::vector::iterator が型、変数 (メンバー)、または関数 (メソッド) のいずれであるかという問題です。gcc で次のエラーが発生します。

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

解決策は、次のようにキーワード「typename」を使用しています。

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...
于 2010-02-27T20:51:58.723 に答える
17

への呼び出しは、int、unsigned int などではなくvector<T>::size()type の値を返します。std::vector<T>::size_type

また、通常、C++ でのコンテナーの反復は、次のようにiteratorsを使用して行われます。

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

ここで、T はベクトルに格納するデータの型です。

または、さまざまな反復アルゴリズム ( 、std::transformstd::copyなど) を使用します。std::fillstd::for_each

于 2009-01-03T16:59:29.620 に答える
12

使用size_t:

for (size_t i=0; i < polygon.size(); i++)

ウィキペディアの引用:

stdlib.h および stddef.h ヘッダー ファイルsize_tは、オブジェクトのサイズを表すために使用される と呼ばれるデータ型を定義します。サイズを取るライブラリ関数は、サイズが であると想定しsize_t、 sizeof 演算子は に評価されsize_tます。

の実際の型size_tはプラットフォームに依存します。よくある間違いは、 が unsigned int と同じであると想定することsize_tです。これは、特に 64 ビット アーキテクチャがより一般的になるにつれて、プログラミング エラーにつながる可能性があります。

于 2009-01-03T16:59:18.933 に答える
7

少し歴史:

数値が負かどうかを表すには、コンピューターで「符号」ビットを使用します。 int正と負の値 (約 -20 億から 20 億) を保持できることを意味する符号付きデータ型です。 Unsignedは正の数のみを格納できます (メタデータを少し無駄にしないため、さらに格納できます: 0 から約 40 億まで)。

std::vector::size()を返しますunsigned。なぜなら、ベクトルの長さが負になる可能性があるからです。

警告は、不等式ステートメントの右側のオペランドが左側よりも多くのデータを保持できることを示しています。

基本的に、20 億を超えるエントリを持つベクトルがあり、インデックスに整数を使用すると、オーバーフローの問題が発生します (int はマイナス 20 億に戻ります)。

于 2009-01-03T17:04:36.817 に答える
6

私は通常 BOOST_FOREACH を使用します。

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

STL コンテナー、配列、C スタイルの文字列などで動作します。

于 2009-01-03T17:23:31.350 に答える
5

C++11 の場合

for_each適切なタイプのイテレータとラムダ式の検索を回避するような一般的なアルゴリズムを使用して、余分な名前付き関数/オブジェクトを回避します。

特定のケースの短い「きれいな」例(ポリゴンが整数のベクトルであると仮定):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

テスト済み: http://ideone.com/i6Ethd

含めることを忘れないでください:アルゴリズムと、もちろんベクトル :)

マイクロソフトは実際にこれに関する良い例も持っています:
ソース: http://msdn.microsoft.com/en-us/library/dd293608.aspx

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}
于 2014-11-12T07:56:44.403 に答える
5

完全を期すために、C++11 構文ではイテレータ ( ref )に対して別のバージョンを 1 つだけ有効にします。

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

逆反復にも快適です

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}
于 2014-10-28T22:11:33.170 に答える
4
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 
于 2009-01-03T17:01:13.983 に答える
2

1 つ目は型が正しく、厳密な意味で正しいです。(考えてみれば、サイズが 0 未満になることはありません。) ただし、この警告は、無視される可能性が高い候補の 1 つに思えます。

于 2009-01-03T17:12:20.873 に答える
0

2 つのコード セグメントは同じように機能します。ただし、unsigned int" ルートは正しいです。unsigned int 型を使用すると、使用したインスタンスのベクターでより適切に機能します。ベクターで size() メンバー関数を呼び出すと、符号なし整数値が返されるため、変数を比較する必要があります。 "i" を独自の型の値に。

また、「unsigned int」がコード内でどのように見えるかについてまだ少し不安がある場合は、「uint」を試してください。これは基本的に「unsigned int」の短縮版であり、まったく同じように機能します。また、それを使用するために他のヘッダーを含める必要もありません。

于 2018-05-29T11:19:54.487 に答える