インデックス変数のタイプの選択


11

ほとんどの場合、インデックス変数を表す整数型を使用します。しかし、状況によっては、選択を余儀なくされます

std::vector<int> vec;
....

for(int i = 0; i < vec.size(); ++i)
....

これにより、コンパイラは、符号付き変数と符号なし変数が混在して使用されているという警告を発します。インデックス変数をとしてfor( size_t i = 0; i < vec.size(); i++ )(またはunsigned int)作成すると、問題が整理されます。

Windowsタイプの使用がより具体的になると、ほとんどのWindows APIはDWORD(unsigned longとしてtypedefされます)を処理します。

したがって、同様の反復を使用すると、再び同じ警告が発生します。今、私はそれを

DWORD dwCount;
....

for(DWORD i = 0; i < dwCount; ++i)
....

これは少し変だと思う。それは知覚の問題かもしれません。

インデックス変数で発生する可能性のある範囲の問題を回避するために、同じタイプのインデックス変数を使用することになっていることに同意します。たとえば、使用している場合

_int64 i64Count; // 
....

for(_int64 i = 0; i < i64Count; ++i)
....

しかし、DWORDまたは符号なし整数の場合、次のように書き換える際に問題がありますか?

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

ほとんどの人はどのように同様の問題に取り組んでいますか?


4
なぜ符号付き整数を使用してインデックスを表すのですか?これは、整数のベクトルを使用して文字列を格納するようなものです。
Let_Me_Be

3
@Let_Me_Be:境界条件をテストしやすくするためです。たとえば、カウントダウンがゼロの場合、ループ本体を実行する前の小なりテストは、符号なしの値では機能しません。同様に、最大値までのカウントは機能しません。もちろん、その場合、符号付き整数も機能しません(そのような大きな値を表すことができないため)。
イットリル

「これにより、コンパイラは、符号付き変数と符号なし変数の混合使用に関する警告を発します。」それはあなたが取り組まなければならない2つの問題のうちの1つにすぎません。多くの場合std::size_t、int(またはlong)よりも高いランクです。ベクトルのサイズがを超える場合、std::numeric_limits<int>::max()intを使用したことを後悔します。
エイドリアンマッカーシー

回答:


11

vectorには、使用する正しい型を伝えるtypedefがあります:-

for(std::vector<int>::size_type i = 0; i < thing.size(); ++i)
{
}

ほとんどの場合、size_tに定義されますが、それに頼ることはできません


8
読みやすさの向上ではありません、私見。
ドックブラウン

いいえ。ただし、ベクトルへのインデックスに使用する正しい型を判断する唯一の方法であるため、それは実際には重要ではありません
...-JohnB

4
C ++ 11だけ使用自動でこれらの日
JohnB

6
@JohnBあなたはどういう意味auto i = 0ですか?それは、すべてのヘルプは、しませんiになりますint
天頂

1
で読みやすさを改善できますusing index_t = std::vector<int>::size_type;
トビーSpeight

4
std::vector<int> vec;

for(int i = 0; i < vec.size(); ++i)

これにはforループではなくイテレータを使用します。

他の人のために、限り、変数の型が同じサイズであるように、static_castうまく動作するはずです(すなわちDWORDint16_t


2
for (std::vector<int>::iterator i = vec.begin(); i != vec.end(); ++i)書くのは苦痛です。持ってfor (auto i = vec.begin();...いるほうがずっと読みやすくなります。もちろん、foreachC ++ 11にもあります。
デビッドソーンリー

3

あなたが説明したケースは、私がC ++で嫌うことの1つでもあります。しかし、私はそれを使って生きることを学びました。

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

または

for( int i = 0; i < (int)vec.size(); i++ )

(もちろん、後者はintオーバーフローが発生するリスクがない場合のみです)。


3

符号付きと符号なしの比較について警告する理由は、符号付きの値が符号なしに変換される可能性が高いためです。

あなたの例では(と比較intしてsize_t)にint暗黙的に変換されますsize_tintどういうわけか範囲がより大きい場合を除くsize_t)。したがって、intが負の場合、ラップアラウンドにより、比較する値よりも大きくなる可能性があります。インデックスが決して負にならない場合、これは問題になりませんが、それでも警告が表示されます。

代わりに、(のような符号なしの型を使用しunsigned intsize_tまたは、としてジョン・Bが推奨するstd::vector<int>::size_typeあなたのインデックス変数のために):

for(unsigned int i = 0; i < vec.size(); i++)

ただし、カウントダウンするときは注意してください:

for(unsigned int i = vec.size()-1; i >= 0; i--) // don't do this!

が署名されていないi >= 0場合iは常に真であるため、上記は機能しません。代わりに、カウントダウンするループに「矢印演算子」を使用します。

for (unsigned int i = vec.size(); i-- > 0; )
    vec[i] = ...;

他の回答が指摘しているように、通常、イテレータを使用してをトラバースしvectorます。C ++ 11構文は次のとおりです。

for (auto i = vec.begin(); i != vec.end(); ++i)

1
それでもunsigned int、サイズを保持するのに十分な大きさではないというリスクがあります。
エイドリアンマッカーシー

2

C ++ 11の新しいオプションで、次のようなことができます

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

そして

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

基本的なforループよりも少し多く繰り返されますが、値を指定する11以前の方法ほど長くはありません。このパターンを使用すると、すべてではないにしてもほとんどの可能な用語で機能します比較したいので、コードのリファクタリングに最適です。次のような単純なケースでも機能します。

int x = 3; int final = 32; for(decltype(final) i = x; i < final; ++i)

さらに、インテリジェントな値(など)にauto設定するときはいつでも使用する必要がありますが、0などの定数に設定するときに機能します。0は単純な整数リテラルであるため、autoはそれを解決します。ivec.begin()decltypeint

正直に言うと、autoループインクリメンタの型判定を拡張して、比較対象の値を調べるコンパイラメカニズムを探したいと思います。


1

のように、intへのキャストを使用しfor (int i = 0; i < (int)v.size(); ++i)ます。はい、それはいです。私は、標準ライブラリの愚かな設計のせいで、サイズを表すために符号なし整数を使用することに決めました。(..何のため?範囲を1ビット拡張しますか?)


1
どんな状況でも、負のコレクションサイズは意味がありますか?さまざまなコレクションサイズに符号なし整数を使用することは、賢明な選択のように思えます。前回私は、文字列の長さはほとんど...どちらか陰性の結果を返さない取って、確認
からCVn

1
if(v.size()-1 > 0) { ... }空のコンテナに対してtrueを返すなどの単純なテストでは、どのような状況で意味がありますか?問題は、特にサイズも計算で使用されることが多いことです。インデックスベースのコンテナを使用します。これは、それらが署名されていないことを考えるとトラブルを求めています。基本的に、1)ビットごとの操作、または2)モジュラー算術以外に符号なしの型を使用すると、問題が発生します。
-zvrba

2
いい視点ね。私はあなたの特定の例のポイントを実際には見ていませんが(それはif(v.size() > 1) { ... }意図をより明確にし、追加のボーナスとして署名/無署名の問題が無効になるため、おそらくただ書くでしょう)、私はいくつかの特定のケースでどのように見えるか署名が役立つ場合があります。私は訂正します。
CVn

1
@Michael:それは不自然な例だと思います。それでも、私はネストされたループでアルゴリズムを書くことがよくあります:for(i = 0; i <v.size()-1; ++ i)for(j = i + 1; j <v.size(); ++ j) .. vが空の場合、外側のループは(size_t)-1回実行されます。そのため、ループの前にv.empty()をチェックするか、v.size()を符号付き型にキャストする必要があります。どちらも個人的にはい回避策だと思います。LOCが少ないのでキャストを選択します。if()s =>ミスの可能性が少ないです。(また、2番目の補数では、変換oveflowは負の数を返すため、ループはまったく実行されません。)
zvrba

範囲を1ビット拡張することは、16ビットシステムでは非常に有用でした(そして、引き続き有効です)。
エイドリアンマッカーシー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.