c++ refactoring

c++ - ¿Cómo manejo las advertencias de "desacuerdo firmado/no firmado"(C4018)?



refactoring (8)

Trabajo con una gran cantidad de código de cálculo escrito en C ++ con un alto rendimiento y una baja sobrecarga de memoria en mente. Utiliza contenedores STL (principalmente vector ) mucho, e itera sobre esos contenedores casi en cada función.

El código iterativo se ve así:

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

pero produce la advertencia de discrepancia firmado / no firmado (C4018 en Visual Studio).

Reemplazar int con algún tipo unsigned es un problema porque frecuentemente usamos pragmas OpenMP, y requiere que el contador sea int .

Estoy a punto de suprimir las (cientos de) advertencias, pero me temo que he echado de menos alguna solución elegante al problema.

En iteradores . Creo que los iteradores son excelentes cuando se aplican en lugares apropiados. El código con el que estoy trabajando nunca cambiará los contenedores de acceso aleatorio en list o algo así (por lo que iterar con int i ya es independiente del contenedor) y siempre necesitará el índice actual. Y todo el código adicional que necesita escribir (el propio iterador y el índice) simplemente complica las cosas y ofusca la simplicidad del código subyacente.


Idealmente, usaría una construcción como esta en su lugar:

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); }

Esto tiene la clara ventaja de que su código de repente se convierte en un contenedor independiente.

Y con respecto a su problema, si alguna biblioteca que utiliza necesita utilizar int donde una unsigned int encajaría mejor, su API es desordenada. De todos modos, si estás seguro de que esos int son siempre positivos, puedes hacer:

int int_distance = static_cast<int>(distance);

Lo cual especificará claramente su intención para el compilador: ya no lo molestará con advertencias.


Puedes usar:

  1. tamaño_t tipo, para eliminar mensajes de advertencia
  2. iteradores + distancia (como la primera pista)
  3. solo iteradores
  4. objeto de función

Por ejemplo:

// 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()); }


Si no puede / no va a utilizar iteradores y si no puede / no va a usar std::size_t para el índice de bucle, realice una función de conversión .size() a int que documente la suposición y realice la conversión explícitamente para silenciar la advertencia del compilador.

#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); }

Entonces escribes tus loops así:

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

La creación de instancias de esta plantilla de función casi seguramente estará en línea. En las compilaciones de depuración, se verificará la suposición. En versiones compiladas, no será así y el código será tan rápido como si llamaras directamente a size (). Ninguna versión producirá una advertencia de compilación, y es solo una ligera modificación del ciclo idiomático.

Si también desea detectar fallas de suposición en la versión de lanzamiento, puede reemplazar la aserción con una instrucción if que arroje algo como std::out_of_range("container size exceeds range of int") .

Tenga en cuenta que esto resuelve tanto la comparación firmado / no firmado como el sizeof(int) potencial de sizeof(int) ! = sizeof(Container::size_type) . Puede dejar todas sus advertencias habilitadas y usarlas para detectar errores reales en otras partes de su código.


También puedo proponer la siguiente solución para C ++ 11.

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

(C ++ no es lo suficientemente inteligente para auto p = 0, entonces tengo que poner p = 0U ...)


Te daré una mejor idea

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

decltype es

Inspecciona el tipo declarado de una entidad o el tipo y categoría de valor de una expresión.

Entonces, deduce el tipo de things.size() y seré un tipo igual que things.size() . Entonces, i < things.size() se ejecutará sin ninguna advertencia


Todo está en tu tipo things.size() . No es int , sino size_t (existe en C ++, no en C) lo que equivale a algún tipo "sin signo" habitual, es decir, unsigned int para x86_32.

El operador "less" (<) no se puede aplicar a dos operandos de diferente signo. Simplemente no existen dichos códigos de operación, y el estándar no especifica si el compilador puede realizar la conversión de signo implícita. Por lo tanto, solo trata el número firmado como no firmado y emite esa advertencia.

Sería correcto escribirlo como

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

o incluso más rápido

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


Tuve un problema similar. Usar size_t no funcionaba. Probé el otro que funcionó para mí. (como a continuación)

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


Yo solo haría

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