14

したがって、プログラム内に、オブジェクトのベクトル(関連する場合は、定義したタイプのオブジェクト)を介した通常のforループがあります。

for(int k = 0; k < objects.size(); k++){ ... }

...そしてコンパイルすると、次の警告が表示されます。

warning: comparison between signed and unsigned integer expressions 

これは理にかなっています。なぜならsize()、ベクトルは。を返すと思うからsize_tです。しかし、なぜそれが重要なのでしょうか?特定の数の要素(またはメモリチャンク)は、数えることができる整数ではありませんか?さらに重要なことに、私のプログラムにはそのようなループが複数あり、セグメンテーション違反が頻繁に発生するため、これはその一部である可能性がありますか?

4

4 に答える 4

11

すでによく答えられていますが、S/0.02を追加します。これを行う「正しい」方法は次のとおりです。

for (typename std::vector<MyObject>::size_type i = 0; i < object.size(); ++i) { ... }

意欲的な言語弁護士だけがそれを書くでしょう、そして彼らでさえ彼らが良いものに到達する前に読むのをやめるでしょう。

C ++ 11を使用すると、次の利点を活用できますdecltype

for (decltype(object.size()) i = 0; i < object.size(); ++i) { ... }

または、以下を利用できますauto

for (auto i = object.size() - object.size(); i < object.size(); ++i) { ... }

または、を使用することもできますが、のsize_typeがsize_tよりも大きい可能性があるsize_tため、オーバーフローについて疑問がある場合があります。vector<MyObject>(そうではありませんが、保証はありません):

for (size_t i = 0; i < object.size(); ++i) { ... }

では、正直なプログラマーは何をするのでしょうか?

絶対に簡単な解決策は、STLが最初から推進してきた解決策です。それを除いて、最初は書くのも苦痛でした:

for (typename std::vector<MyObject>::iterator_type it = object.begin(); it != object.end(); ++it) { ... }

さて、C++11は実際にあなたを助けます。単純なものから始めて、いくつかの非常に優れた選択肢があります。

for (auto it = object.begin(); it != object.end(); ++it) { ... }

しかし、それはさらに良くなります(ドラムロールをお願いします)...:

for (auto& val : object) { ... }

そして、それは私が使用するものです。


追加するために編集:

Cory Nelsonはコメントの中で、object.end()の結果を次のようにキャッシュすることも可能であると指摘しています。

for (auto it = object.begin(), end = object.end(); it != end; ++it) { ... }

構文によって生成されたコードは、for (var : object)CoryNelsonによって提案されたものと非常に似ていることがわかります。(だから私は彼とあなたに後者を使うことを勧めます。)

ただし、元の投稿の主題であった反復を含め、他のセマンティクスとは微妙に異なるセマンティクスがあります。反復中にコンテナのサイズを変更するように変更する場合は、慎重に検討する必要があります。災害の可能性が高いです。

反復中に変更される可能性のあるベクトルを反復する唯一の方法は、元の投稿のように整数インデックスを使用することです。他のコンテナはより寛容です。反復ごとにobject.end()を呼び出すループを使用してSTLマップを反復できます。(私が知る限り)挿入や削除を行っても機能しますが、unordered_mapでは試さないでください。またはベクトル。常に最後を押して前をポップする場合は、両端キューで機能します。これは、幅優先探索で両端キューをキューとして使用する場合に便利です。後ろの両端キューをポップすることで逃げることができるかどうかはわかりません。

これはすべて標準で指定されているため、イテレーターと要素ポインター(イテレーターと常に同じとは限りません)に対するコンテナータイプのコンテナー変更による影響の簡単な要約が実際にどこかにあるはずですが、私はそれに遭遇したことはありませんどこでも。見つけたら教えてください。

于 2012-10-16T00:46:54.667 に答える
11

この問題は、表現可能な最大値である。よりも大きいobject.size()値を返す場合に発生します。が署名されているため、最大値は1に比べて半分になります。kksize_t

現在、これは特定のアプリケーションでは発生しない可能性があります(通常の32ビットシステムでは、コレクション内のオブジェクトは20億以上になります)が、常に正しいタイプを使用することをお勧めします。

1.先制的反論:はい、これは一般的な2の補数演算を使用するマシン、intおよびとsize_tが同じビット数を使用して表されるマシンにのみ当てはまります。

于 2012-10-16T00:23:07.080 に答える
3

ほとんどの場合、ベクトルにsignedintが表すことができるよりも多くの要素が含まれている点に到達するまでは問題ではありません。

于 2012-10-16T00:23:19.013 に答える
2

重要な警告は、ループ変数の符号付き/符号なしの比較に関する警告のストリームで失われる可能性があります。そして、いくつかの符号付き/符号なしの比較警告でさえ重要です!したがって、次のようにサイズ関数を定義して、重要でない警告を取り除きます。

#include <stddef.h>    // ptrdiff_t
#include <utility>     // std::begin, std::end

typedef ptrdiff_t Size;
typedef Size Index;

template< class Type >
Size nElements( Type const& c )
{
    using std::begin;  using std::end;
    return end( c ) - begin( c );
}

次に、あなたはただ書くことができます例えば

for( int i = 0;  i < nElements( v );  ++i ) { ... }

または、イテレータを使用します

for( auto it = begin( v );  it != end( v );  ++it ) { ... }

および/またはC++11範囲ベースのforループを使用します。

for( auto const& elem : v ) { ... }

とにかく、これらのsegfaultやその他のバグを取り除くには、実用的な最高の警告レベルでクリーンなコンパイルを取得することが重要です。

そのために注目すべきもう1つの領域は、Cスタイルのキャストです。;-)

于 2012-10-16T00:41:36.187 に答える