web-dev-qa-db-ja.com

forループでstd :: size_tまたはintを使用する必要がありますか?

intの代わりにstd::size_tをループやものに使用すべきかと思っています。例えば:

#include <cstdint>

int main()
{
    for (std::size_t i = 0; i < 10; ++i) {
        // std::size_t OK here? Or should I use, say, unsigned int instead?
    }
}

一般的に、std::size_tを使用する場合のベストプラクティスは何ですか?

181
nhaa123

良い経験則は、ループ条件で自然にstd::size_tそのものであるものと比較する必要があるものすべてに適用されます。

std::size_tは任意のsizeof式のタイプであり、C++で任意のオブジェクト(配列を含む)の最大サイズを表現できることが保証されています。拡張により、任意の配列インデックスに対して十分な大きさであることが保証されるため、配列のインデックスによるループの自然なタイプです。

数を数えるだけの場合、その数を保持する変数の型、またはintまたはunsigned int(十分な大きさの場合)を使用する方が自然かもしれません。マシンの自然なサイズ。

168
CB Bailey

size_tは、sizeof演算子の結果タイプです。

配列のサイズまたはインデックスをモデル化する変数には、size_tを使用します。 size_tはセマンティクスを伝えます。これは、別の整数ではなく、バイト単位のサイズまたはインデックスを表すことをすぐに理解できます。

また、size_tを使用してサイズをバイト単位で表すと、コードを移植しやすくなります。

69
Gregory Pakosz

size_tタイプは、何かのsizeを指定するためのものです。たとえば、文字列の長さを取得してから各文字を処理するなど、使用するのが自然です。

for (size_t i = 0, max = strlen (str); i < max; i++)
    doSomethingWith (str[i]);

doは、境界条件に注意する必要があります。これは、符号なしの型であるためです。通常、最大値は大きいので、上端の境界はそれほど重要ではありません(ただし、isそこに到達できます)。ほとんどの人は、そのようなことに対してintを使用します。なぜなら、そのintの容量を超えるほど十分に大きくなる構造体や配列を持つことはめったにないからです。

ただし、次のようなことに注意してください。

for (size_t i = strlen (str) - 1; i >= 0; i--)

これは、符号なしの値のラップ動作のために無限ループを引き起こします(コンパイラーがこれに対して警告するのを見たことがありますが)。これは次の方法でも軽減できます(理解するのは少し難しいですが、少なくともラッピングの問題は免れます)。

for (size_t i = strlen (str); i-- > 0; )

デクリメントを継続条件のチェック後の副作用にシフトすることにより、値before decrementの継続をチェックしますが、ループ内でデクリメントされた値を使用します(これが理由です)ループはlen .. 1ではなくlen-1 .. 0から実行されます。

30
paxdiablo

定義により、size_tsizeof演算子の結果です。 size_tは、サイズを参照するために作成されました。

あなたが何かをする回数(あなたの例では10回)はサイズではないので、なぜsize_tを使用するのですか? int、またはunsigned intは問題ありません。

もちろん、ループ内でiを使用して行うことも重要です。たとえば、unsigned intを受け取る関数に渡す場合は、unsigned intを選択します。

いずれにせよ、暗黙的な型変換を避けることをお勧めします。 すべての型変換を明示的にする。

12
Daniel Daranas

size_tは、文字列の長さ、ポインタがとるバイト数など、アイテムのサイズディメンションを指定する非常に読みやすい方法です。プラットフォーム間でも移植可能です。64ビットと32ビットの両方が適切に動作することがわかります。システム関数とsize_t-unsigned intではできないかもしれないこと(たとえば、いつunsigned longを使用すべきか)

10
Ofir

Cスタイルの配列のインデックス作成/カウントにはstd :: size_tを使用します。

STLコンテナーの場合、(たとえば)vector<int>::size_typeがあります。これは、ベクトル要素のインデックス付けとカウントに使用する必要があります。

実際には、通常は両方とも符号なし整数ですが、特にカスタムアロケーターを使用する場合は保証されません。

8
Peter Alexander

間もなく、ほとんどのコンピューターは64ビットOSを備えた64ビットアーキテクチャになり、数十億の要素のコンテナーで動作するプログラムを実行します。次に、mustループインデックスとしてintの代わりにsize_tを使用する必要があります。そうでない場合、インデックスは32ビットシステムと64ビットシステムの両方で、2 ^ 32:th要素でラップアラウンドします。

未来に備えましょう!

8
Nordlöw

簡潔な答え:

ほとんどは決してない

長い答え:

32ビットシステムで2GBを超えるcharのベクトルが必要な場合。他のすべてのユースケースでは、符号なしのタイプを使用するよりも、符号付きのタイプを使用する方がはるかに安全です。

例:

std::vector<A> data;
[...]
// calculate the index that should be used;
size_t i = calc_index(param1, param2);
// doing calculations close to the underflow of an integer is already dangerous

// do some bounds checking
if( i - 1 < 0 ) {
    // always false, because 0-1 on unsigned creates an underflow
    return LEFT_BORDER;
} else if( i >= data.size() - 1 ) {
    // if i already had an underflow, this becomes true
    return RIGHT_BORDER;
}

// now you have a bug that is very hard to track, because you never 
// get an exception or anything anymore, to detect that you actually 
// return the false border case.

return calc_something(data[i-1], data[i], data[i+1]);

size_tと同等の署名は、intではなくptrdiff_tです。ただし、ほとんどの場合、intを使用するほうが、size_tよりもはるかに優れています。 ptrdiff_tは、32ビットおよび64ビットシステムではlongです。

これは、std :: containersを操作するたびに、size_tとの間で常に変換する必要があることを意味します。これはあまり美しくありません。しかし、進行中のネイティブ会議で、c ++の作者は、署名されていないsize_tでstd :: vectorを設計するのは間違いだと述べました。

コンパイラーがptrdiff_tからsize_tへの暗黙的な変換に関する警告を表示する場合、コンストラクター構文で明示的にすることができます。

calc_something(data[size_t(i-1)], data[size_t(i)], data[size_t(i+1)]);

境界チェックなしでコレクションを繰り返したい場合は、以下に基づいて範囲を使用します:

for(const auto& d : data) {
    [...]
}

going native のBjarne Stroustrup(C++作成者)からの言葉

一部の人々にとって、STLのこの署名された/署名されていない設計エラーは、std :: vectorを使用せず、代わりに独自の実装を使用するのに十分な理由です。

7
Arne

Size_tを使用する場合は、次の式に注意してください

size_t i = containner.find("mytoken");
size_t x = 99;
if (i-x>-1 && i+x < containner.size()) {
    cout << containner[i-x] << " " << containner[i+x] << endl;
}

Xの値に関係なく、if式でfalseになります。問題の原因を理解するのに数分しかかかりませんが、これを実現するのに数日かかりました(コードは非常に単純なので、単体テストは行いませんでした)。キャストを実行するか、ゼロを使用することをお勧めします。

if ((int)(i-x) > -1 or (i-x) >= 0)

両方の方法が機能するはずです。これが私のテスト実行です

size_t i = 5;
cerr << "i-7=" << i-7 << " (int)(i-7)=" << (int)(i-7) << endl;

出力:i-7 = 18446744073709551614(int)(i-7)=-2

他のコメントをお願いします。

4
Kemin Zhou

size_tは、さまざまなライブラリによって返され、そのコンテナのサイズがゼロ以外であることを示します。あなたが一度戻ってきたときにそれを使用します:0

ただし、上記の例では、size_tでのループは潜在的なバグです。以下を考慮してください。

for (size_t i = thing.size(); i >= 0; --i) {
  // this will never terminate because size_t is a typedef for
  // unsigned int which can not be negative by definition
  // therefore i will always be >= 0
  printf("the never ending story. la la la la");
}

符号なし整数を使用すると、この種の微妙な問題が発生する可能性があります。したがって、私はsize_tを必要とするコンテナ/タイプと対話する場合にのみsize_tを使用することを好みます。

2
ascotan