Question

Lequel des suivants est le meilleur et pourquoi? (Particulier à 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
}

J'ai vu des conseils pour (a) à cause de la fonction call to length. Cela me dérange. Aucun compilateur moderne ne fait-il que l'optimisation de (b) soit similaire à celle de (a)?

Était-ce utile?

La solution

L'exemple (b) a un sens différent de l'exemple (a) et le compilateur doit l'interpréter au fur et à mesure que vous l'écrivez.

Si, pour une raison quelconque à laquelle je n'arrive pas à penser, j'ai écrit du code pour le faire:

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

Je n'aurais vraiment pas voulu que le compilateur optimise chaque appel à vec.length (), car j'obtiendrais des résultats différents.

Autres conseils

j'aime bien:

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

Bien sûr, cela fonctionnerait également pour les itérateurs:

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

J'aime ça parce que c'est à la fois efficace (appeler end() une seule fois) et relativement succinct (ne taper que vector<int>::const_iterator une fois).

Je suis surpris que personne n'ait dit l'évidence:

Dans 99,99% des cas, cela n'a pas d'importance.

Sauf si vous utilisez un conteneur où le calcul size() est une opération coûteuse, il est inconcevable que votre programme ralentisse même de quelques nanosecondes. Je dirais que vous devez vous en tenir au plus lisible jusqu’à ce que vous profiliez votre code et constatiez que <=> est un goulot d’étranglement.

Il y a deux questions à débattre ici:

  1. La variable scope
  2. Réévaluation de la condition de fin

Portée variable

Normalement, vous n’auriez pas besoin que la variable de boucle soit visible en dehors de la boucle. C'est pourquoi vous pouvez le déclarer dans la for construction.

Réévaluation de la condition de fin

Andrew Shepherd l'a bien dit: cela signifie quelque chose de différent de placer un appel de fonction dans la condition finale:

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 ) {
}

Pourquoi est-ce que ça vous gêne? Ces deux alternatives ne voient pas la même chose. L’une effectue un nombre fixe d’itérations, l’autre dépend du corps des boucles.

Une autre alternative possible

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

Sauf si vous avez besoin de la variable de boucle en dehors de la boucle, la deuxième approche est préférable.

Les itérateurs vous donneront une performance aussi bonne ou meilleure. (Il y avait un gros fil de comparaison sur comp.lang.c ++. Modéré il y a quelques années).

De plus, j'utiliserais

int i = 0;

Plutôt que le constructeur de la syntaxe que vous utilisez. Bien que valide, ce n'est pas idiomatique.

Un peu en relation:

Avertissement: Comparaison entre les entiers signés et non signés.

Le type correct pour les index de tableau et de vecteur est size_t .

Parlant strictement , en C ++, il en est même std::vector<>::size_type.

Le nombre de développeurs C / C ++ ayant encore mal compris celui-ci est étonnant.

Voyons sur le code généré (j'utilise MSVS 2008 avec optimisation complète).

a.

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

La boucle for produit 2 instructions d'assembleur.

b.

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

La boucle for produit 8 instructions d'assembleur. vec.size () est intégré avec succès.

c.

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

La boucle for produit 15 instructions d'assembleur (tout est en ligne, mais le code comporte beaucoup de sauts)

Donc, si votre application est critique en termes de performances, utilisez a). Sinon b) ou c).

Il convient de noter que les exemples d'itérateurs:

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

peut invalider l'itérateur de la boucle "it" si le corps de la boucle provoque la réallocation du vecteur. Donc, ce n'est pas équivalent à

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

où le corps de la boucle ajoute des éléments à vec.

Question simple: modifiez-vous vec dans la boucle?

La réponse à cette question conduira également à votre réponse.

jrh

Il est très difficile pour un compilateur de placer l'appel vec.length() en sécurité, sachant qu'il est constant, à moins qu'il ne soit en ligne (ce qui, espérons-le, le sera souvent!). Mais au moins i doit absolument être déclaré dans le deuxième style & "; B &" ;, même si l'appel length doit être & "Manuellement &"; hissé hors de la boucle!

Celui-ci est préférable:

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)
}
  • Je peux tout de suite dire que le vecteur n'est pas en cours de mise à jour
  • n'importe qui peut dire ce qui se passe ici
  • Je sais combien de boucles
  • v.end() retourne le pointeur un après le dernier élément donc il n'y a pas de frais généraux de vérifier la taille
  • facile à mettre à jour pour différents conteneurs ou types de valeur

(b) ne calculera pas / n'appellera pas la fonction à chaque fois.

- commencez l'extrait ----

Code Motion Invariant Boucle: GCC inclut le mouvement de code invariant de boucle dans son optimiseur de boucle ainsi que dans sa passe d'élimination de la redondance partielle. Cette optimisation supprime les instructions des boucles, qui calculent une valeur qui ne change pas pendant la durée de vie d’une boucle.

--- extrait final -

Autres optimisations pour gcc:

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

Pourquoi ne pas éviter complètement le problème avec BOOST_FOREACH

#include <boost/foreach.hpp>

std::vector<double> vec;

//...

BOOST_FOREACH( double &d, vec)
{
    std::cout << d;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top