web-dev-qa-db-ja.com

Std :: vectorの反復:符号なし対符号付きインデックス変数

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変数が正しく使われないなら危険であることを知っています、そうです - これは正しいですか?

433
Yuval Adam

後方反復

この回答 を参照してください。

前方への反復

これはほとんど同じです。イテレータを変更するか、増分でスワップデクリメントします。イテレータを好むべきです。インデックス変数の型としてstd::size_tを使用するように言う人もいます。ただし、それは移植性がありません。常にコンテナのsize_type typedefを使用します(前方反復の場合は変換のみで済ますことができますが、std::size_tを使用すると、 case std::size_tは、size_typeのtypedefよりも広いです:

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アプローチがどのような問題をもたらす可能性があるかについては、後方反復回答をお読みください。

762

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

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

163
kratenko

あなたの例の特定のケースでは、私はこれを達成するために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 );
44
paxos1977

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) {
...
38
Polat Tuzla

vector<T>::size()を呼び出すと、int、unsigned intなどではなく、タイプstd::vector<T>::size_typeの値が返されます。

また、一般的にC++のコンテナの反復処理は、 イテレータ を使用して次のように行われます。

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

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

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

あるいは、異なる反復アルゴリズム(std::transformstd::copystd::fillstd::for_eachなど)を使用します。

16
Jasper Bekkers

size_tを使用してください。

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

引用 ウィキペディア

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

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

11
Igor Oks

ちょっとした歴史:

数値が負かどうかを表すには、コンピュータは「符号」ビットを使用します。 intは符号付きデータ型で、正の値と負の値を持つことができます(約-2億〜20億)。 Unsignedは正数のみを格納できます(そしてメタデータを多少浪費しないので0:約40億)。

std::vector::size()unsignedを返します、なぜベクトルは負の長さを持つことができるのでしょうか?

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

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

6
ecoffey

私は通常BOOST_FOREACHを使います:

#include <boost/foreach.hpp>

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

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

6
Martin Cote

完全にするために、C++ 11構文では、イテレータ用にもう1つのバージョン( ref )が有効になります。

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
}
5
Jan Turoň

C++ 11の場合

私はfor_eachのような一般的なアルゴリズムを使って、正しい型のイテレータを探したり、余分な名前付き関数やオブジェクトを使わないようにしたりします。

あなたの特定のケースのための短い「かわいい」例(多角形が整数のベクトルであると仮定します):

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

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

include: algorithm、そしてもちろんvector :)を忘れないでください。

マイクロソフトは実際にこれについてもいい例を持っています:
ソース: http://msdn.Microsoft.com/ja-jp/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;
}
5
jave.web
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 
4
Mehrdad Afshari

1つ目は型が正しいこと、そして厳密な意味で正しいことです。 (あなたがそう考えるならば、サイズは決してゼロより小さくすることはできません。)その警告は無視されるための良い候補の一つとして私を襲います。

2
Charlie Martin

まったく反復する必要があるかどうかを検討してください

<algorithm>標準ヘッダーは、このための機能を提供します。

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

アルゴリズムライブラリの他の関数は一般的なタスクを実行します - あなたがあなた自身の努力を節約したいならばあなたが利用可能なものを知っていることを確認してください。

2
Toby Speight

あいまいだが重要な詳細:あなたが以下のように "for(auto it)"と言うと、実際の要素ではなくオブジェクトのコピーを得る。

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.Push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

ベクトルの要素を修正するには、イテレータを参照として定義する必要があります。

for(auto &it : v)
0
Pierre

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

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

0
user9758081

コンパイラでサポートされている場合は、ベクトル要素にアクセスするためにに基づく範囲を使用できます。

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

1 2 3を印刷します。このテクニックを使ってベクトルの要素を変更することはできません。

0
Brett L