2010-11-23 40 views
9

En C++ modifier une variable de boucle à l'intérieur d'une boucle for est autorisé:Comment montrer qu'une variable de contrôle de boucle n'est pas modifiée dans le corps de la boucle for C++?

for(int i = 0; i < limit; i++) { 
    if(condition) { 
     i--; 
    } 
} 

Maintenant, si un corps de la boucle est assez complexe, il est pas évident au lecteur si la variable de boucle est modifiée dans la boucle corps. Il serait bien de modifier le code de manière à ce que, une fois que le lecteur ne voit que l'en-tête, il sache immédiatement que la variable de boucle n'est pas modifiée dans le corps.

Par exemple, si j'utilise const:

const int value = computeValue(); 
//lots of code here 

alors il est clair que quel que soit le code est écrit ci-dessous la définition de la variable const la variable est inchangée.

Existe-t-il un moyen d'obtenir quelque chose de similaire - la constance logique au sein de l'itération - dans le cas de variables de contrôle for-loop en C++?

+3

Je crois que 'std :: for_each' pourrait faire cela. – kennytm

+0

J'ai utilisé 'BOOST_FOREACH' avec beaucoup de succès. Vous pouvez même introduire un utilitaire '#define foreach BOOST_FOREACH' dans votre en-tête précompilé. –

Répondre

1

Il n'y a aucune construction logique pour l'appliquer. Si vous mettez 'const int idx = i' comme première instruction dans la boucle, et n'utilisez ensuite que 'idx', vous pourrez peut-être parvenir à une application similaire, mais à la perte de clarté. Sinon, utilisez simplement des commentaires.

0

Vous pouvez essayer de changer votre nom de variable "loop" en quelque chose de long et ridicule, de sorte que le toucher à l'intérieur de la boucle (plus d'une fois) va rayer l'oeil.

Également certains aiment utiliser des macros dédiées de boucle, telles que BOOST_FOREACH. Ceci cache la variable de boucle/l'itérateur.

4

Je ne pense pas que cela soit possible pour une boucle fabriquée à la main, mais j'imagine que cela pourrait être considéré comme un argument supplémentaire pour encourager l'utilisation de std::for_each et BOOST_FOREACH pour les itérations sur le conteneur STL.

EDIT ... et le C++ 0x range-based for-loop (merci Matthieu. M :)

+0

+1, beaucoup mieux et plus propre que la façon dont je l'ai fait! :) –

+0

Si l'on veut garantir la non-modification du compteur de boucle dans le corps de la boucle, alors pour l'amour de Dieu * faites-le *. J'ai montré un sens dans ma réponse, il y en a d'autres. Mais ne faites pas cette chose Rube Goldberg qui ne réalise même pas le but. –

+0

@Alf: évidemment, comme indiqué par les étiquettes "solution stupide" dans ma réponse, je ne recommanderais jamais quelque chose comme ça dans le code «réel» – icecrime

2

Ceci est une grande question et m'a fait penser à la façon dont vous pourriez faire et des moyens autour d'elle. Sans passer trop de temps, la seule chose que je suis venu avec était:

int limit = 10; 
for(int i(0), guard(0); i < limit; i++, guard++) 
{ 
     // lots of code here 
    assert(i == guard); 
} 

De toute évidence, l'utilisateur peut toujours modifier la garde dans la boucle, mais peut-être le inention en haut montre qu'il va vérifier.

1

Techniquement, vous pourriez le faire, par exemple.

int main() 
{ 
    for(int i = 0; i < 42; ++i) 
    {{ 
     typedef void i; 

     i = 666;  // !Nope. 
    }} 
} 

Si vous souhaitez accéder à i à l'intérieur du corps de la boucle que vous auriez alors à fournir un autre nom (en référence à const) avant la typedef.

Mais je ne recommande pas cette solution technique, car elle est obscure et pas commune, donc pas évident pour le lecteur.

Au lieu de cela, il suffit de refactoriser les grandes boucles.:-)

1

Vous pouvez faire du corps entier de la boucle for une fonction distincte, pour laquelle la variable de contrôle de boucle est hors de portée.

Je ne peux pas penser à un moyen simple de faire ce que vous voulez puisque la variable de contrôle de boucle est mutable de définition, afin de contrôler la boucle.

2

C++ 0x amusant. Ce code n'est pas compilé:

 
for (int i = 0; i < 10; ++i) 
{ 
    [&, i]() 
    { 
     if (i == 5) 
     { 
      ++i; 
     } 
     cout << i << endl; 
    }(); 
} 

Erreur: « i »: une par valeur saisie ne peut pas être modifiée dans un lambda non mutables

2

Utilisation for_each combiné avec boost::counting_iterator et une fonction qui accepte un const int.

for_each(boost::counting_iterator<int>(0), boost::counting_iterator<int>(limit), 
    [&](const int i) 
    { 
     // do something with i 
    }); 
+0

Je pense qu'avec C++ 0x, vous pouvez faire ça ' typedef boost :: counting_iterator bcii; pour (const int i: std :: make_pair (bcii (0), bcii (limite))) {...} ' –

1

Créer un objet étrange avec une macro qui prend FILE et LINE, ce dernier peut-être en tant que paramètre de modèle (il est une constante de compilation?). Comme vous l'incrémentez, il doit utiliser le même FICHIER et LIGNE. Eh bien, la même ligne suffira probablement. Si ce n'est pas sur la même ligne, vous pourriez obtenir une erreur de compilation.

template< int line > 
class Counter 
{ 
// stuff 
public: 
    bool valid() const; 
    static void inc(Counter<line> & counter); 
}; 

for(Counter<__LINE__> counter(n); counter.valid(); Counter<__LINE__>::inc(counter)) 
{ 
// body 

    // what __LINE__ do I need to use here to increment counter? Can be done but won't be 
} 

Je ne l'ai pas testé. Juste une idée.