「符号付き/符号なしの不一致」警告(C4018)に対処するにはどうすればよいですか?


80

私は、高性能と低メモリオーバーヘッドを念頭に置いて、C ++で記述された多くの計算コードを使用しています。STLコンテナー(ほとんどvector)を頻繁に使用し、ほぼすべての関数でそのコンテナーを反復処理します。

反復コードは次のようになります。

for (int i = 0; i < things.size(); ++i)
{
    // ...
}

ただし、符号付き/符号なしの不一致の警告が生成されます(Visual StudioのC4018)。

OpenMPプラグマを頻繁に使用するため、intあるunsignedタイプに置き換えることは問題であり、カウンターはint。である必要があります。

私は(数百の)警告を抑制しようとしていますが、問題に対するいくつかのエレガントな解決策を見逃しているのではないかと心配しています。

イテレータについて。イテレータは適切な場所に適用すると素晴らしいと思います。私が使用しているコードは、ランダムアクセスコンテナを何かに変更することなくlist(したがって、反復処理int iはすでにコンテナに依存しません)、常に現在のインデックスが必要になります。また、入力する必要のあるすべての追加コード(イテレーター自体とインデックス)は、問題を複雑にし、基になるコードの単純さを難読化します。


1
OpenMPプラグマがunsigned型の使用を妨げる例を投稿できますか?これによるとそれはだけでなく、あらゆるタイプのインターガルで機能するはずintです。
Billy ONeal 2011

4
この質問はstackoverflowに適していると思います。
bcsanches 2011

1
intまたstd::vector<T>::size_type、サイズや署名が異なる場合があります。たとえば、LLP64システム(64ビットWindowsなど)では、sizeof(int) == 4ただしsizeof(std::vector<T>::size_type) == 8
エイドリアンマッカーシー


回答:


60

それはすべてあなたのthings.size()タイプです。そうではありませんintsize_t(CではなくC ++に存在します)、これは「通常の」符号なし型、つまりunsigned intx86_32に相当します。

演算子「less」(<)は、符号の異なる2つのオペランドには適用できません。そのようなオペコードはなく、標準では、コンパイラが暗黙的な符号変換を実行できるかどうかは指定されていません。したがって、符号付き数値を符号なしとして扱い、その警告を発します。

次のように書くのが正しいでしょう

for (size_t i = 0; i < things.size(); ++i) { /**/ }

またはさらに高速

for (size_t i = 0, ilen = things.size(); i < ilen; ++i) { /**/ }

17
-1いいえ、そうではありませんsize_t。それはstd::vector< THING >::size_typeです。
Raedwald 2011

8
@Raedwald:あなたが技術的に正しいですが、それが標準規格に準拠した実装が異なるため根本的なタイプで終わる可能性がどのように想像するのは難しいstd::size_tstd::vector<T>::size_type
エイドリアンマッカーシー

4
なぜ++ iが優れていると考えられているのですか?forループでは「違いはない」のではないですか?
Shoaib 2014年

2
@ShoaibHaider、戻り値が使用されていないプリミティブの場合はまったく問題ありません。ただし、カスタムタイプ(演算子がオーバーロードされている場合)の場合、ポストインクリメントはほとんどの場合効率が低くなります(インクリメントされる前にオブジェクトのコピーを作成する必要があるため)。コンパイラーは(必然的に)カスタム型を最適化することはできません。したがって、唯一の利点は(プリミティブとカスタムタイプの)一貫性です。
Kat 2014

2
@zenith:はい、その通りです。私のステートメントは、デフォルトのアロケータにのみ当てはまります。カスタムアロケータはstd :: size_t以外のものを使用する場合がありますが、それでも符号なし整数型である必要があり、std :: size_tよりも広い範囲を表すことはできないと思われるため、stdを使用しても安全です。ループインデックスのタイプとして:: size_t。
エイドリアンマッカーシー

13

理想的には、代わりに次のような構成を使用します。

for (std::vector<your_type>::const_iterator i = things.begin(); i != things.end(); ++i)
{
  // if you ever need the distance, you may call std::distance
  // it won't cause any overhead because the compiler will likely optimize the call
  size_t distance = std::distance(things.begin(), i);
}

これには、コードが突然コンテナに依存しなくなるという優れた利点があります。

そして、あなたの問題に関して、あなたが使用するいくつかのライブラリがあなたがより適したint場所で使用することを要求するならunsigned int、それらのAPIは乱雑です。とにかく、それらintが常にポジティブであると確信しているなら、あなたはただするかもしれません:

int int_distance = static_cast<int>(distance);

これにより、コンパイラに対する意図が明確に指定されます。警告でバグが発生することはもうありません。


1
私はいつも距離必要です。static_cast<int>(things.size())他にないのなら、たぶん解決策かもしれません。
Andrew T

@Andrew:警告を抑制することにした場合、最善の方法は#pragma warning(push) #pragma warning(disable: 4018) /* ... function */ #pragma warning(pop)、不必要なキャストを使用するのではなく、コンパイラ固有のプラグマ(MSVCではこれが)を使用することです。(キャストは正当なエラーを隠します、m'kay?;))
Billy ONeal 2011

違います。キャスト!=変換。警告は、安全でない可能性のある標準で許可されている暗黙の変換に関する警告です。しかし、キャストは、警告によって最初に話されたものよりも安全ではない可能性があるパーティーへの明示的な回心を招きます。厳密に言えば、少なくともx86では、変換はまったく行われません。プロセッサは、正しい命令を使用して作業する限り、メモリの特定の部分を符号付きまたは符号なしとして扱っているかどうかを気にしません。
Billy ONeal 2011

コンテナに依存しない原因である場合にはO(N ^ 2)複雑さが(そしてあなたの例では、それは(距離として、ありません)リストのためのO(N)<>です)、私はそうではないよ必ずそれが利点である:-(。
No-

@ No-BugsHareそれがまさにポイントです。確信が持てません。OPに要素がほとんどない場合、それはおそらく素晴らしいことです。彼がそれらを何百万も持っているなら、おそらくそれほど多くはないでしょう。最終的にはプロファイリングだけがわかりますが、良いニュースは、保守可能なコードをいつでも最適化できることです。
ereOn 2018年

9

イテレータを使用できない/使用std::size_tしない場合、およびループインデックスを使用できない/使用しない場合は、仮定を文書化し、コンパイラの警告を消すために明示的に変換を行う.size()toint変換関数を作成します。

#include <cassert>
#include <cstddef>
#include <limits>

// When using int loop indexes, use size_as_int(container) instead of
// container.size() in order to document the inherent assumption that the size
// of the container can be represented by an int.
template <typename ContainerType>
/* constexpr */ int size_as_int(const ContainerType &c) {
    const auto size = c.size();  // if no auto, use `typename ContainerType::size_type`
    assert(size <= static_cast<std::size_t>(std::numeric_limits<int>::max()));
    return static_cast<int>(size);
}

次に、次のようにループを記述します。

for (int i = 0; i < size_as_int(things); ++i) { ... }

この関数テンプレートのインスタンス化は、ほぼ確実にインライン化されます。デバッグビルドでは、仮定がチェックされます。リリースビルドでは、そうではなく、コードはsize()を直接呼び出した場合と同じくらい高速になります。どちらのバージョンもコンパイラの警告を生成せず、慣用的なループをわずかに変更するだけです。

リリースバージョンでも仮定の失敗をキャッチしたい場合は、アサーションをstd::out_of_range("container size exceeds range of int")。のようなものをスローするifステートメントに置き換えることができます。

これにより、符号付き/符号なしの比較と潜在的なsizeof(int)!=sizeof(Container::size_type)問題の両方が解決されることに注意してください。すべての警告を有効のままにして、それらを使用してコードの他の部分の実際のバグをキャッチできます。


6

次を使用できます。

  1. size_tタイプ、警告メッセージを削除します
  2. イテレータ+距離(最初のヒントのように)
  3. イテレータのみ
  4. 関数オブジェクト

例えば:

// simple class who output his value
class ConsoleOutput
{
public:
  ConsoleOutput(int value):m_value(value) { }
  int Value() const { return m_value; }
private:
  int m_value;
};

// functional object
class Predicat
{
public:
  void operator()(ConsoleOutput const& item)
  {
    std::cout << item.Value() << std::endl;
  }
};

void main()
{
  // fill list
  std::vector<ConsoleOutput> list;
  list.push_back(ConsoleOutput(1));
  list.push_back(ConsoleOutput(8));

  // 1) using size_t
  for (size_t i = 0; i < list.size(); ++i)
  {
    std::cout << list.at(i).Value() << std::endl;
  }

  // 2) iterators + distance, for std::distance only non const iterators
  std::vector<ConsoleOutput>::iterator itDistance = list.begin(), endDistance = list.end();
  for ( ; itDistance != endDistance; ++itDistance)
  {
    // int or size_t
    int const position = static_cast<int>(std::distance(list.begin(), itDistance));
    std::cout << list.at(position).Value() << std::endl;
  }

  // 3) iterators
  std::vector<ConsoleOutput>::const_iterator it = list.begin(), end = list.end();
  for ( ; it != end; ++it)
  {
    std::cout << (*it).Value() << std::endl;
  }
  // 4) functional objects
  std::for_each(list.begin(), list.end(), Predicat());
}

3

C ++ 11の次のソリューションを提案することもできます。

for (auto p = 0U; p < sys.size(); p++) {

}

(C ++はautop = 0に対して十分に賢くないので、p = 0Uを置く必要があります...)


1
C ++ 11の場合は+1。C ++ 11を使用できない正当な理由がない限り、新しい機能を使用するのが最善だと思います...それらは大きな助けになることを意図しています。そしてfor (auto thing : vector_of_things)、実際にインデックスが必要ない場合は、間違いなく使用してください。
parker.sikand 2016

しかし、それは符号の問題だけを解決します。size()非常に一般的なunsignedintより大きい型を返す場合は役に立ちません。
エイドリアンマッカーシー

3

私はあなたにもっと良い考えを与えるでしょう

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

decltype です

エンティティの宣言されたタイプ、または式のタイプと値のカテゴリを検査します。

したがって、のタイプを推測し、things.size()i同じタイプになりますthings.size()。したがって、 i < things.size()警告なしに実行されます


0

私も同様の問題を抱えていました。size_tの使用は機能していませんでした。私は私のために働いた他のものを試しました。(以下のように)

for(int i = things.size()-1;i>=0;i--)
{
 //...
}

0

私はただやる

int pnSize = primeNumber.size();
for (int i = 0; i < pnSize; i++)
    cout << primeNumber[i] << ' ';
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.