std :: vectorの繰り返し:符号なしインデックス変数と符号付きインデックス変数

StackOverflow https://stackoverflow.com/questions/409348

  •  03-07-2019
  •  | 
  •  

質問

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変数は少々恐ろしく見えます。<=>変数は正しく使用しないと危険な場合があるので、これは正しいですか?

役に立ちましたか?

解決

後方への反復

この回答

フォワードの反復

これはほとんど同じです。イテレータを変更するか、増分でスワップデクリメントします。イテレータを好むべきです。一部の人々は、インデックス変数タイプとしてstd::size_tを使用するように言っています。ただし、それは移植性がありません。常にコンテナのsize_type typedefを使用します(前方反復の場合は変換だけで済ますことができますが、実際にはsizeofを使用する場合、後方反復の場合は間違ってしまう可能性があります<=> <=>)の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(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

インデックスの使用

<*>

しかし、<=>アプローチがどのような問題を引き起こす可能性があるかについて、逆方向の反復的な答えを読んでください。

他のヒント

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 gnu c ++ コンパイラに固有であり、プログラムはプラットフォーム(およびコンパイラ)である必要があることに注意してください )独立。

この例の特定のケースでは、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 );

ヨハネスシャウブの答えについて:

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) {
...

vector<T>::size()を呼び出すと、int、unsigned intなどではなく、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::copystd::fillstd::for_eachなど)を使用します。

size_tを使用:

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

引用ウィキペディア

  

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

     

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

通常はBOOST_FOREACHを使用します:

#include <boost/foreach.hpp>

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

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

少しの歴史:

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

std::vector::size()unsignedを返します。これは、ベクトルの長さが負になる可能性があるためです。

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

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

完全にするために、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
}

C ++ 11で

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

短い<!> quot; pretty <!> quot;特定のケースの例(ポリゴンが整数のベクトルであると仮定):

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

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

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

Microsoftは実際にこれに関する良い例も持っています:
ソース: 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;
}
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 

最初は正しい型であり、厳密な意味で正しい型です。 (考えてみれば、サイズをゼロより小さくすることはできません。)しかし、この警告は無視されるのにふさわしい候補の1つだと思います。

反復する必要があるかどうかを検討します

<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);

アルゴリズムライブラリの他の関数は一般的なタスクを実行します-労力を節約したい場合は、利用可能なものを必ず確認してください。

不明瞭だが重要な詳細:<!> quot; for(auto it)<!> quot;次のように、実際の要素ではなく、オブジェクトのコピーを取得します。

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)

コンパイラがサポートしている場合、範囲に基づいてベクター要素にアクセスできます:

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

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

印刷:1 2 3。ベクトルの要素を変更するためにこの手法を使用することはできません。

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

また、<!> quot; unsigned int <!> quot;がまだ少し不安な場合は、コードを調べて、<!> quot; uint <!> quot;を試してください。これは基本的に<!> quot; unsigned int <!> quotの短縮バージョンです。まったく同じように機能します。また、使用するために他のヘッダーを含める必要はありません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top