Come faccio a gestire gli avvisi di “mancata corrispondenza firmati / non firmati” (C4018)?

Io lavoro con un sacco di codice di calcolo scritto in C ++, tenendo conto delle alte prestazioni e del sovraccarico di memoria. Usa molto i contenitori STL (principalmente il vector ), e scorre su quei contenitori quasi in ogni singola funzione.

Il codice iteratore si presenta così:

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

ma produce l’avviso di mancata corrispondenza firmato / non firmato (C4018 in Visual Studio).

Sostituire int con alcuni tipi di unsigned è un problema perché utilizziamo frequentemente i prmap di OpenMP e richiede che il contatore sia int .

Sto per sopprimere le (centinaia di) avvertenze, ma temo di non aver trovato una soluzione elegante al problema.

Su iteratori . Penso che gli iteratori siano grandi se applicati in posti appropriati. Il codice con cui sto lavorando non cambierà mai i contenitori ad accesso casuale in list o qualcosa (quindi iterando con int i è già indipendente dal contenitore) e avrà sempre bisogno dell’indice corrente. E tutto il codice aggiuntivo che devi digitare (lo stesso iteratore e l’indice) complica le cose e offusca la semplicità del codice sottostante.

È tutto nel tuo tipo things.size() . Non è int , ma size_t (esiste in C ++, non in C) che è uguale ad un tipo “normale” non firmato, cioè unsigned int per x86_32.

L’operatore “less” (<) non può essere applicato a due operandi di segno diverso. Non ci sono solo questi codici opzionali e lo standard non specifica se il compilatore può effettuare la conversione implicita dei segni. Quindi tratta solo il numero firmato come non firmato ed emette tale avviso.

Sarebbe corretto scriverlo come

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

o ancora più veloce

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

Idealmente, userei invece un costrutto come questo:

 for (std::vector::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); } 

Questo ha il vantaggio che il tuo codice diventa improvvisamente agnostico del contenitore.

Per quanto riguarda il problema, se alcune librerie utilizzate richiedono l’uso di int cui un unsigned int si adatta meglio, la loro API è disordinata. Ad ogni modo, se sei sicuro che quelle int sono sempre positive, puoi semplicemente fare:

 int int_distance = static_cast(distance); 

Che specificherà chiaramente il tuo intento al compilatore: non ti disturberà più con gli avvertimenti.

Se non puoi / non userai iteratori e se non puoi / non userai std::size_t per l’indice del ciclo, crea una funzione di conversione .size() in int che documenta l’ipotesi e fa esplicitamente la conversione per silenziare l’avviso del compilatore.

 #include  #include  #include  // 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  /* 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::numeric_limits::max())); return static_cast(size); } 

Quindi scrivi i tuoi loop in questo modo:

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

L'istanziazione di questo modello di funzione sarà quasi certamente in linea. Nelle build di debug, l'ipotesi verrà verificata. Nelle versioni di rilascio, non lo sarà e il codice sarà veloce come se si chiamasse size () direttamente. Nessuna delle due versioni produrrà un avvertimento sul compilatore, ed è solo una leggera modifica al ciclo idiomatico.

Se si desidera rilevare errori di assunzione nella versione di rilascio, è ansible sostituire l'asserzione con un'istruzione if che genera qualcosa come std::out_of_range("container size exceeds range of int") .

Si noti che questo risolve sia il confronto firmato / non firmato, sia il problema potenziale sizeof(int) ! = sizeof(Container::size_type) . Puoi lasciare abilitati tutti gli avvisi e utilizzarli per individuare bug reali in altre parti del tuo codice.

Puoi usare:

  1. size_t type, per rimuovere i messaggi di avvertimento
  2. iteratori + distanza (come se fossero il primo suggerimento)
  3. solo iteratori
  4. object di funzione

Per esempio:

 // 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 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::iterator itDistance = list.begin(), endDistance = list.end(); for ( ; itDistance != endDistance; ++itDistance) { // int or size_t int const position = static_cast(std::distance(list.begin(), itDistance)); std::cout << list.at(position).Value() << std::endl; } // 3) iterators std::vector::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()); } 

Ti darò un’idea migliore

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

decltype è

Ispeziona il tipo dichiarato di un' quadro o il tipo e la categoria di valore di un'espressione.

Quindi, deduce il tipo di things.size() e sarò un tipo uguale a things.size() . Quindi, i < things.size() verrà eseguito senza alcun avviso

Posso anche proporre la seguente soluzione per C ++ 11.

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

(C ++ non è abbastanza intelligente per auto p = 0, quindi devo mettere p = 0U ....)

Ho avuto un problema simile. L’utilizzo di size_t non funzionava. Ho provato l’altro che ha funzionato per me. (come sotto)

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

Lo farei e basta

 int pnSize = primeNumber.size(); for (int i = 0; i < pnSize; i++) cout << primeNumber[i] << ' ';