¿Cuál es la forma correcta de iterar sobre un vector en C ++?
Considere estos dos fragmentos de código, este funciona bien:
for (unsigned i=0; i < polygon.size(); i++) {
sum += polygon[i];
}
y éste:
for (int i=0; i < polygon.size(); i++) {
sum += polygon[i];
}
que genera warning: comparison between signed and unsigned integer expressions
.
Soy nuevo en el mundo de C ++, por lo que la unsigned
variable me parece un poco aterradora y sé que las unsigned
variables pueden ser peligrosas si no se usan correctamente, entonces, ¿es esto correcto?
.size()
no es del tipounsigned
akaunsigned int
. Es de tipostd::size_t
.std::size_t
es una definición de tipo definida por la implementación. Ver el estándar.std::size_t
puede ser equivalente aunsigned
en su implementación actual, pero eso no es relevante. Fingir que puede resultar en código no portátil y comportamiento indefinido.std::size_t
en la práctica. ¿Crees que ya hemos cubierto todo en este torrente de comentarios durante más de 6 años?Respuestas:
Para iterar hacia atrás, vea esta respuesta .
Iterar hacia delante es casi idéntico. Simplemente cambie los iteradores / decremento de intercambio por incremento. Deberías preferir iteradores. Algunas personas le dicen que use
std::size_t
como tipo de variable de índice. Sin embargo, eso no es portátil. Siempre use elsize_type
typedef del contenedor (Si bien podría salirse con la suya solo con una conversión en el caso de iteración hacia adelante, en realidad podría salir mal en el caso de iteración hacia atrás cuando se usastd::size_t
, en caso de questd::size_t
sea más ancho de lo que es el typedefsize_type
) :Usando std :: vector
Usando iteradores
Lo importante es que siempre use el formulario de incremento de prefijo para los iteradores cuyas definiciones no conoce. Eso asegurará que su código se ejecute lo más genérico posible.
Usando Range C ++ 11
Usando índices
Usar matrices
Usando iteradores
Usando Range C ++ 11
Usando índices
Sin
sizeof
embargo, lea en la respuesta iterativa hacia atrás a qué problema puede dar lugar el enfoque.fuente
for (auto p : polygon){sum += p;}
Pasaron cuatro años, Google me dio esta respuesta. Con el estándar C ++ 11 (también conocido como C ++ 0x ), existe una nueva forma agradable de hacerlo (al precio de romper la compatibilidad con versiones anteriores): la nueva
auto
palabra clave. Le ahorra la molestia de tener que especificar explícitamente el tipo de iterador a usar (repitiendo el tipo de vector nuevamente), cuando es obvio (para el compilador), qué tipo usar. Conv
ser tuvector
, puedes hacer algo como esto:C ++ 11 va más allá y le brinda una sintaxis especial para iterar sobre colecciones como vectores. Elimina la necesidad de escribir cosas que siempre son iguales:
Para verlo en un programa de trabajo, cree un archivo
auto.cpp
:Al escribir esto, cuando compila esto con g ++ , normalmente necesita configurarlo para que funcione con el nuevo estándar dando una bandera adicional:
Ahora puedes ejecutar el ejemplo:
Tenga en cuenta que las instrucciones para compilar y ejecutar son específicas del compilador gnu c ++ en Linux , el programa debe ser independiente de la plataforma (y el compilador).
fuente
for (auto& val: vec)
std::vector<int> v = std::vector<int>();
, o podría simplemente haber usado en sustd::vector<int> v;
lugar?En el caso específico de su ejemplo, usaría los algoritmos STL para lograr esto.
Para un caso más general, pero aún bastante simple, elegiría:
fuente
Con respecto a la respuesta de Johannes Schaub:
Eso puede funcionar con algunos compiladores pero no con gcc. El problema aquí es la pregunta si std :: vector :: iterator es un tipo, una variable (miembro) o una función (método). Recibimos el siguiente error con gcc:
La solución está usando la palabra clave 'typename' como se dice:
fuente
T
trata de un argumento de plantilla y, por lo tanto, la expresiónstd::vector<T*>::iterator
es un nombre dependiente. Para que un nombre dependiente se analice como un tipo, debe estar precedido por latypename
palabra clave, como lo indica el diagnóstico.Una llamada a
vector<T>::size()
devuelve un valor de tipostd::vector<T>::size_type
, no int, unsigned int u otro.También, en general, la iteración sobre un contenedor en C ++ se realiza mediante iteradores , como este.
Donde T es el tipo de datos que almacena en el vector.
O el uso de los diferentes algoritmos de iteración (
std::transform
,std::copy
,std::fill
,std::for_each
etc.).fuente
Uso
size_t
:Citando Wikipedia :
fuente
#include <cstddef>
lugar de<stddef.h>
o, peor aún, la totalidad[c]stdlib
y el uso enstd::size_t
lugar de la versión no calificada, y lo mismo para cualquier otra situación en la que pueda elegir entre<cheader>
y<header.h>
.Un poco de historia:
Para representar si un número es negativo o no, la computadora utiliza un bit de "signo".
int
es un tipo de datos con signo, lo que significa que puede contener valores positivos y negativos (alrededor de -2 mil millones a 2 mil millones).Unsigned
solo puede almacenar números positivos (y dado que no desperdicia un poco en los metadatos, puede almacenar más: de 0 a aproximadamente 4 mil millones).std::vector::size()
devuelve ununsigned
, porque ¿cómo podría un vector tener longitud negativa?La advertencia le dice que el operando derecho de su declaración de desigualdad puede contener más datos que el izquierdo.
Esencialmente, si tiene un vector con más de 2 mil millones de entradas y usa un número entero para indexar, encontrará problemas de desbordamiento (el int volverá a los 2 mil millones negativos).
fuente
Usualmente uso BOOST_FOREACH:
Funciona en contenedores STL, matrices, cadenas de estilo C, etc.
fuente
Para completar, la sintaxis de C ++ 11 habilita una versión más para los iteradores ( ref ):
Que también es cómodo para la iteración inversa
fuente
En C ++ 11
Usaría algoritmos generales como
for_each
evitar evitar buscar el tipo correcto de iterador y expresión lambda para evitar funciones / objetos con nombre adicionales.El breve ejemplo "bonito" para su caso particular (suponiendo que el polígono es un vector de enteros):
probado en: http://ideone.com/i6Ethd
No olvide incluir: algoritmo y, por supuesto, vector :)
Microsoft también tiene un buen ejemplo de esto:
fuente: http://msdn.microsoft.com/en-us/library/dd293608.aspx
fuente
fuente
El primero es el tipo correcto y correcto en un sentido estricto. (Si lo piensa, el tamaño nunca puede ser inferior a cero). Sin embargo, esa advertencia me parece uno de los buenos candidatos para ser ignorado.
fuente
i == INT_MAX
, luegoi++
causa un comportamiento indefinido. En este punto, puede pasar cualquier cosa.Considere si necesita iterar
El
<algorithm>
encabezado estándar nos proporciona facilidades para esto:Otras funciones en la biblioteca de algoritmos realizan tareas comunes: asegúrese de saber qué hay disponible si desea ahorrar esfuerzo.
fuente
Detalle oscuro pero importante: si dice "for (auto it)" de la siguiente manera, obtendrá una copia del objeto, no del elemento real:
Para modificar los elementos del vector, debe definir el iterador como referencia:
fuente
Si su compilador lo admite, puede usar un rango basado para acceder a los elementos vectoriales:
Impresiones: 1 2 3. Tenga en cuenta que no puede usar esta técnica para cambiar los elementos del vector.
fuente
Los dos segmentos de código funcionan igual. Sin embargo, la ruta unsigned int "es correcta. El uso de los tipos unsigned int funcionará mejor con el vector en la instancia en que lo usó. Llamar a la función de miembro size () en un vector devuelve un valor entero sin signo, por lo que desea comparar la variable "i" a un valor de su propio tipo.
Además, si todavía está un poco incómodo acerca de cómo se ve "unsigned int" en su código, pruebe con "uint". Esta es básicamente una versión abreviada de "unsigned int" y funciona exactamente igual. Tampoco necesita incluir otros encabezados para usarlo.
fuente
Agregando esto ya que no pude encontrarlo mencionado en ninguna respuesta: para la iteración basada en índices, podemos usar el
decltype(vec_name.size())
que evaluaríastd::vector<T>::size_type
Ejemplo
fuente