Pregunta

¿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 este:

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 variable unsigned me parece un poco aterradora y sé que <=> las variables pueden ser peligrosas si no se usan correctamente, entonces, ¿es esto correcto?

¿Fue útil?

Solución

Iterando hacia atrás

Ver esta respuesta .

Iterando hacia adelante

Esto 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 el tipo de variable de índice. Sin embargo, eso no es portátil. Siempre use el size_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 usa sizeof, en el caso <=> es más ancho de lo que es el typedef de <=>):

Usando std :: vector

Uso de iteradores

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

Importante es, 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

for(auto const& value: a) {
     /* std::cout << value; ... */

Uso de índices

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Uso de matrices

Uso de iteradores

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Usando Range C ++ 11

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Uso de índices

<*>

Lea en la respuesta iterativa hacia atrás a qué problema puede dar lugar el enfoque <=>,

Otros consejos

Pasaron cuatro años, Google me dio esta respuesta. Con el estándar C ++ 11 (también conocido como C ++ 0x ) hay una nueva forma agradable de hacerlo (al precio de romper la compatibilidad con versiones anteriores): la nueva palabra clave auto. 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. Con v siendo tu vector, puedes hacer algo como esto:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C ++ 11 va más allá y le ofrece una sintaxis especial para iterar sobre colecciones como vectores. Elimina la necesidad de escribir cosas que siempre son iguales:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

Para verlo en un programa de trabajo, cree un archivo auto.cpp:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

Al escribir esto, cuando compila esto con g ++ , normalmente necesita configurarlo para que funcione con el nuevo estándar dando una bandera adicional:

g++ -std=c++0x -o auto auto.cpp

Ahora puedes ejecutar el ejemplo:

$ ./auto
17
12
23
42

Tenga en cuenta que las instrucciones para compilar y ejecutar son específicas del compilador gnu c ++ en Linux , el programa debe ser plataforma (y compilador ) independiente.

En el caso específico de su ejemplo, usaría los algoritmos STL para lograr esto.

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

Para un caso más general, pero aún bastante simple, iría con:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

Con respecto a la respuesta de Johannes Schaub:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

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:

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

La solución está usando la palabra clave 'typename' como se dice:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...

Una llamada a vector<T>::size() devuelve un valor de tipo std::vector<T>::size_type, no int, unsigned int u otro.

También, en general, la iteración sobre un contenedor en C ++ se realiza utilizando iteradores , como este.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Donde T es el tipo de datos que almacena en el vector.

O usando los diferentes algoritmos de iteración (std::transform, std::copy, std::fill, std::for_each etcétera).

Use size_t:

for (size_t i=0; i < polygon.size(); i++)

Citando Wikipedia :

  

Los archivos de encabezado stdlib.h y stddef.h definen un tipo de datos llamado <=> que se utiliza para representar el tamaño de un objeto. Las funciones de biblioteca que toman tamaños esperan que sean del tipo <=>, y el operador sizeof se evalúa como <=>.

     

El tipo real de <=> depende de la plataforma; un error común es asumir que <=> es lo mismo que unsigned int, lo que puede conducir a errores de programación, particularmente a medida que las arquitecturas de 64 bits se vuelven más frecuentes.

Usualmente uso BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

Funciona en contenedores STL, matrices, cadenas de estilo C, etc.

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 metadatos, puede almacenar más: de 0 a aproximadamente 4 mil millones).

std::vector::size() devuelve un unsigned, ¿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, tendrá problemas de desbordamiento (el int volverá a los 2 mil millones negativos).

Para completar, la sintaxis de C ++ 11 habilita una versión más para los iteradores ( ref ):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

Que también es cómodo para la iteración inversa

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}

En C ++ 11

Usaría algoritmos generales como for_each para evitar buscar el tipo correcto de iterador y expresión lambda para evitar funciones / objetos adicionales con nombre.

El corto " bonito " ejemplo para su caso particular (suponiendo que el polígono es un vector de enteros):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

probado en: http://ideone.com/i6Ethd

No se olvide de 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

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 

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.

Considera si necesitas iterar en absoluto

El encabezado estándar <algorithm> nos proporciona facilidades para esto:

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

Otras funciones en la biblioteca de algoritmos realizan tareas comunes: asegúrese de saber qué hay disponible si desea ahorrar esfuerzo.

Detalles oscuros pero importantes: si dice " for (auto it) " de la siguiente manera, obtienes una copia del objeto, no del elemento real:

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

Para modificar los elementos del vector, debe definir el iterador como referencia:

for(auto &it : v)

Si su compilador lo admite, puede usar un rango basado para acceder a los elementos del vector:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

Impresiones: 1 2 3. Tenga en cuenta que no puede usar esta técnica para cambiar los elementos del vector.

Los dos segmentos de código funcionan igual. Sin embargo, unsigned int & Quot; La ruta es correcta. El uso de tipos int sin signo funcionará mejor con el vector en la instancia en que lo usó. Llamar a la función miembro size () en un vector devuelve un valor entero sin signo, por lo que desea comparar la variable & Quot; i & Quot; a un valor de su propio tipo.

Además, si todavía está un poco incómodo acerca de cómo " unsigned int " mira en su código, intente " uint " ;. Esta es básicamente una versión abreviada de & Quot; unsigned int & Quot; y funciona exactamente igual Tampoco necesita incluir otros encabezados para usarlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top