c++ stl unsigned signed

c++ - Iteración sobre std:: vector: sin signo vs. variable de índice firmada



stl unsigned (16)

¿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 una warning: comparison between signed and unsigned integer expressions .

Soy nuevo en el mundo de C ++, por lo que la variable unsigned parece un poco aterradora y sé que las variables unsigned pueden ser peligrosas si no se usan correctamente, ¿es correcto?


Considere si necesita 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é está disponible si desea ahorrar esfuerzo.


En C ++ 11

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

El breve ejemplo "bonito" 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 olvides incluir: algoritmo y, por supuesto, vector :)

Microsoft también tiene un buen ejemplo en 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; }


Iterando hacia atrás

Ver esta respuesta

Iterando hacia adelante

Esto es casi idéntico. Solo cambia los iteradores / swap decremento por incremento. Deberías preferir los iteradores. Algunas personas le dicen que use std::size_t como tipo de variable de índice. Sin embargo, eso no es portátil. Utilice siempre el typedef del tipo size_type del contenedor (aunque solo podría realizar 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 al usar std::size_t , en caso de que std::size_t es más ancho que lo que es el typedef de size_type ):

Usando std :: vector

Utilizando iteradores

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

Es importante, 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.

Utilizando Range C ++ 11

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

Utilizando índices

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

Utilizando matrices

Utilizando iteradores

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

Utilizando Range C ++ 11

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

Utilizando índices

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

Sin embargo, lea en la respuesta iterativa al revés a qué problema se puede producir el enfoque de sizeof .


El primero es el tipo correcto, y correcto en algún sentido estricto. (Si lo piensas, el tamaño nunca puede ser menor que cero). Sin embargo, esa advertencia me parece uno de los buenos candidatos para ser ignorado.


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, me gustaría ir con:

#include <boost/lambda/lambda.hpp> #include <boost/lambda/bind.hpp> using namespace boost::lambda; std::for_each( polygon.begin(), polygon.end(), sum += _1 );


Los dos segmentos de código funcionan de la misma manera. Sin embargo, la ruta int sin signo es correcta. El uso de tipos int sin signo funcionará mejor con el vector en la instancia que lo usó. Al llamar a la función miembro size () en un vector, se obtiene 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 no está seguro de cómo se ve "unsigned int" en su código, intente "uint". Esta es básicamente una versión abreviada de "unsigned int" y funciona exactamente igual. Tampoco es necesario incluir otros encabezados para usarlo.


Obscuro pero importante detalle: si dice "para (automáticamente)" de la siguiente manera, obtiene 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, necesita definir el iterador como una referencia:

for(auto &it : v)


Para completar, la sintaxis de C ++ 11 habilita solo 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 }


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 hacer esto (al precio de romper la compatibilidad con versiones anteriores): la nueva palabra clave auto . Le ahorra el dolor de tener que especificar explícitamente el tipo de iterador que se debe usar (repetir 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 aún más lejos y le brinda una sintaxis especial para iterar sobre colecciones como vectores. Se elimina la necesidad de escribir cosas que son siempre las mismas:

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

Para verlo en un programa de trabajo, construya 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; }

A la hora de escribir esto, cuando compila esto con g ++ , normalmente necesita configurarlo para que funcione con el nuevo estándar dando una marca 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 sobre compilación y ejecución son específicas del compilador gnu c ++ en Linux , el programa debe ser independiente de la plataforma (y del compilador).


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). Obtenemos 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á utilizando la palabra clave ''typename'' como se indica:

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


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

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 utilizar esta técnica para cambiar los elementos del vector.


Un poco de historia:

Para representar si un número es negativo o no, la computadora usa 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 billones a 2 billones). Unsigned solo puede almacenar números positivos (y como no se desperdicia ni 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 una longitud negativa?

La advertencia le indica que el operando derecho de su declaración de desigualdad puede contener más datos que la izquierda.

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 se redondeará a 2 mil millones negativos).


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

También, generalmente, la iteración sobre un contenedor en C ++ se realiza mediante 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.).


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 estilo C, etc.


Utilice 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 size_t que se usa para representar el tamaño de un objeto. Las funciones de biblioteca que toman tamaños esperan que sean del tipo size_t , y el operador sizeof evalúa a size_t .

El tipo real de size_t depende de la plataforma; Un error común es asumir que size_t es lo mismo que unsigned int, lo que puede dar lugar a errores de programación, en especial a medida que prevalecen las arquitecturas de 64 bits.


for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++) sum += *it;