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:
- tamaño_t tipo, para eliminar mensajes de advertencia
- iteradores + distancia (como la primera pista)
- solo iteradores
- 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] << '' '';