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
変数が正しく使われないなら危険であることを知っています、そうです - これは正しいですか?
この回答 を参照してください。
これはほとんど同じです。イテレータを変更するか、増分でスワップデクリメントします。イテレータを好むべきです。インデックス変数の型としてstd::size_t
を使用するように言う人もいます。ただし、それは移植性がありません。常にコンテナのsize_type
typedefを使用します(前方反復の場合は変換のみで済ますことができますが、std::size_t
を使用すると、 case std::size_t
は、size_type
のtypedefよりも広いです:
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
/* std::cout << *it; ... */
}
重要なのは、定義がわからないイテレータには常にプレフィックスインクリメント形式を使用することです。これにより、コードを可能な限り汎用的に実行できます。
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; ... */
}
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
アプローチがどのような問題をもたらす可能性があるかについては、後方反復回答をお読みください。
4年が経ち、Googleがこの答えをくれました。 標準C++ 11 (別名C++ 0x)実際には、これを行うための新しい快適な方法があります(後方互換性を壊すことを犠牲にして):新しいauto
キーワード。使用するタイプが明らかな場合(コンパイラーにとって)、使用するイテレーターのタイプを明示的に指定する(ベクトルタイプを再度繰り返す)必要性を軽減します。 v
をvector
にすると、次のようなことができます。
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上のコンパイラでは、プログラムはプラットフォーム(およびコンパイラ)に依存しない必要があります。
あなたの例の特定のケースでは、私はこれを達成するために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 );
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) {
...
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::transform
、std::copy
、std::fill
、std::for_each
など)を使用します。
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ビットアーキテクチャが普及するにつれて、プログラミングエラーを引き起こす可能性があります。
ちょっとした歴史:
数値が負かどうかを表すには、コンピュータは「符号」ビットを使用します。 int
は符号付きデータ型で、正の値と負の値を持つことができます(約-2億〜20億)。 Unsigned
は正数のみを格納できます(そしてメタデータを多少浪費しないので0:約40億)。
std::vector::size()
はunsigned
を返します、なぜベクトルは負の長さを持つことができるのでしょうか?
警告は、不等式ステートメントの右側のオペランドが左側よりも多くのデータを保持できることを示しています。
基本的に、20億を超えるエントリを持つベクトルがあり、インデックスに整数を使用すると、オーバーフローの問題が発生します(intは負の20億に戻ります)。
私は通常BOOST_FOREACHを使います:
#include <boost/foreach.hpp>
BOOST_FOREACH( vector_type::value_type& value, v ) {
// do something with 'value'
}
STLコンテナ、配列、Cスタイルの文字列などで動作します。
完全にするために、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
}
私は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;
}
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);
アルゴリズムライブラリの他の関数は一般的なタスクを実行します - あなたがあなた自身の努力を節約したいならばあなたが利用可能なものを知っていることを確認してください。
あいまいだが重要な詳細:あなたが以下のように "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)
2つのコードセグメントは同じように機能します。ただし、unsigned int型のルートは正しいです。unsigned int型を使用すると、使用したインスタンスのベクトルでうまく機能します。ベクトルに対してsize()メンバ関数を呼び出すと、符号なし整数値が返されるので、変数を比較します。 "i"はそれ自身の型の値になります。
また、 "unsigned int"がコード内でどのように見えるかについて少し不安がある場合は、 "uint"を試してください。これは基本的に "unsigned int"の短縮版で、まったく同じように機能します。それを使用するために他のヘッダを含める必要もありません。
コンパイラでサポートされている場合は、ベクトル要素にアクセスするためにに基づく範囲を使用できます。
vector<float> vertices{ 1.0, 2.0, 3.0 };
for(float vertex: vertices){
std::cout << vertex << " ";
}
1 2 3を印刷します。このテクニックを使ってベクトルの要素を変更することはできません。