Pregunta

¿Cuál de los siguientes es mejor y por qué? (Particular a c ++)

a.

int i(0), iMax(vec.length());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
  //loop body
}

b.

for( int i(0);i < vec.length(); ++i)
{
  //loop body
}

He visto consejos para (a) debido a la función call to length. Esto me está molestando. ¿No hace algún compilador moderno la optimización de (b) para que sea similar a (a)?

¿Fue útil?

Solución

El ejemplo (b) tiene un significado diferente al ejemplo (a), y el compilador debe interpretarlo mientras lo escribe.

Si, (por alguna razón inventada que no se me ocurre), escribí un código para hacer esto:

for( int i(0);i < vec.length(); ++i)
{
    if(i%4 == 0)
       vec.push_back(Widget());
}

Realmente no hubiera querido que el compilador optimizara cada llamada a vec.length (), porque obtendría resultados diferentes.

Otros consejos

Me gusta:

for (int i = 0, e = vec.length(); i != e; ++i)

Por supuesto, esto también funcionaría para iteradores:

for (vector<int>::const_iterator i = v.begin(), e = v.end(); i != e; ++i)

Me gusta esto porque es eficiente (llamando end() solo una vez) y también relativamente breve (solo tiene que escribir vector<int>::const_iterator una vez).

Me sorprende que nadie haya dicho lo obvio:

En el 99.99% de los casos, no importa.

A menos que esté utilizando algún contenedor donde calcular size() sea una operación costosa, es insondable que su programa vaya incluso unos pocos nanosegundos más lento. Yo diría que quédate con lo más legible hasta que perfiles tu código y descubras que <=> es un cuello de botella.

Hay dos cuestiones para debatir aquí:

  1. El alcance variable
  2. La reevaluación de la condición final

Alcance variable

Normalmente, no necesitaría que la variable del bucle fuera visible fuera del bucle. Es por eso que puede declararlo dentro de la for construcción.

Reevaluación de condición final

Andrew Shepherd lo dijo amablemente: significa algo diferente poner una llamada de función dentro de la condición final:

for( vector<...>::size_type i = 0; i < v.size(); ++i ) { // vector size may grow.
   if( ... ) v.push_back( i ); // contrived, but possible
}

// note: this code may be replaced by a std::for_each construct, the previous can't.
for( vector<...>::size_type i = 0, elements = v.size(); i != elements; ++i ) {
}

¿Por qué te agobia? Esas dos alternativas no parecen estar haciendo lo mismo. Uno está haciendo un número fijo de iteraciones, mientras que el otro depende del cuerpo del bucle.

Otra alternativa de colud sea

for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
 //loop body
}

A menos que necesite la variable de bucle fuera del bucle, es preferible el segundo enfoque.

Los iteradores realmente te darán un rendimiento tan bueno o mejor. (Hubo un gran hilo de comparación en comp.lang.c ++. Moderado hace unos años).

Además, usaría

int i = 0;

En lugar del constructor como la sintaxis que estás usando. Si bien es válido, no es idiomático.

Algo no relacionado:

Advertencia: Comparación entre entero con signo y sin signo.

El tipo correcto para la matriz y los índices vectoriales es size_t.

Estrictamente hablando, en C ++ es incluso std::vector<>::size_type.

Increíble cuántos desarrolladores de C / C ++ todavía se equivocan.

Veamos el código generado (uso MSVS 2008 con optimización completa).

a.

int i(0), iMax(vec.size());//vec is a container, say std::vector
for(;i < iMax; ++i)
{
  //loop body
}

El bucle for produce 2 instrucciones de ensamblador.

b.

for( int i(0);i < vec.size(); ++i)
{
  //loop body
}

El bucle for produce 8 instrucciones de ensamblador. vec.size () se ha insertado correctamente.

c.

for (std::vector<int>::const_iterator i = vec.begin(), e = vec.end(); i != e; ++i)
{
  //loop body
}

El bucle for produce 15 instrucciones de ensamblador (todo está en línea, pero el código tiene muchos saltos)

Entonces, si su aplicación es crítica para el rendimiento, utilice a). De lo contrario b) o c).

Cabe señalar que los ejemplos de iterador:

for (vector<T>::iterator it=vec.begin();it!=vec.end();it++){
 //loop body
}

podría invalidar el iterador de bucle 'it' si el cuerpo del bucle causa que el vector se reasigne. Por lo tanto, no es equivalente a

for (int i=0;i<vec.size();++i){
 //loop body
}

donde el cuerpo del bucle agrega elementos a vec.

Pregunta simple: ¿estás modificando vec en el ciclo?

la respuesta a esta pregunta también dará lugar a su respuesta.

jrh

Es muy difícil para un compilador izar la llamada vec.length() sabiendo que es constante, a menos que esté en línea (¡lo que con suerte lo hará a menudo!). Pero al menos i definitivamente debe declararse en el segundo estilo & Quot; b & Quot ;, incluso si la llamada length necesita ser & Quot; manualmente & Quot; izada fuera del bucle!

Este es preferible:

typedef vector<int> container; // not really required,
                               // you could just use vector<int> in for loop

for (container::const_iterator i = v.begin(); i != v.end(); ++i)
{
    // do something with (*i)
}
  • Puedo decir de inmediato que el vector no se actualiza
  • cualquiera puede decir lo que está sucediendo aquí
  • Sé cuántos bucles
  • v.end() devuelve el puntero más allá del último elemento para que no haya gastos generales de comprobar el tamaño
  • fácil de actualizar para diferentes contenedores o tipos de valor

(b) no calculará / llamará a la función cada vez.

- comenzar extracto ----

Movimiento de código de bucle invariante: GCC incluye movimiento de código invariante de bucle como parte de su optimizador de bucle, así como en su pase de eliminación de redundancia parcial. Esta optimización elimina las instrucciones de los bucles, que calculan un valor que no cambia durante la vida útil de un bucle.

--- extracto final -

Más optimizaciones para gcc:

https://www.in.redhat.com/software /gnupro/technical/gnupro_gcc.php3

¿Por qué no eludir el problema por completo con BOOST_FOREACH

#include <boost/foreach.hpp>

std::vector<double> vec;

//...

BOOST_FOREACH( double &d, vec)
{
    std::cout << d;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top