web-dev-qa-db-fra.com

Raccourci pour for-loop - sucre syntaxique en C++ (11)

En fait, ce sont deux questions liées.

Je sais qu'il existe une nouvelle syntaxe en C++ 11 pour les boucles for basées sur des plages de la forme:

//v is some container
for (auto &i: v){
   // Do something with i
}

Première question: comment puis-je déduire à quelle itération je suis dans cette boucle? (Disons que je veux remplir un vecteur avec la valeur j à la position j).

Deuxième question: je voulais savoir s’il existait un autre moyen d’écrire une boucle de la forme

for (int i=0; i<100; i++) { ... }

Je trouve cette façon de l'écrire un peu lourde, et je le fais si souvent et j'aimerais avoir une syntaxe plus concise pour cela ..

for(i in [0..99]){ ... }

serait génial.

Pour les deux questions, j'aimerais éviter d'avoir à utiliser des bibliothèques supplémentaires.

13
dingalapadum

Première réponse: vous ne le faites pas. Vous avez utilisé une construction simple dans un but simple. vous aurez besoin de quelque chose de plus compliqué si vous avez des besoins plus compliqués.

Deuxième réponse: vous pouvez créer un type d'itérateur qui génère des valeurs entières consécutives et un type "conteneur" qui en donne une plage Sauf si vous avez une bonne raison de le faire vous-même, Boost a une telle chose :

#include <boost/range/irange.hpp>

for (int i : boost::irange(0,100)) {
    // i goes from 0 to 99 inclusive
}
20
Mike Seymour

Utilisez ceci:

size_t pos = 0;
for (auto& i : v) {
    i = pos;
    ++pos;
}

( Boost est bon, mais ce n'est pas universellement accepté.)

14
Peter K

Pour la première question, la réponse est assez simple: si vous avez besoin du nombre d'itérations, n'utilisez pas la construction syntaxique qui supprime le nombre d'itérations. Utilisez simplement une boucle for normale et non la boucle basée sur la plage.

Pour la deuxième question, je ne pense pas qu'il y ait actuellement quelque chose dans la bibliothèque standard, mais vous pouvez utiliser un boost::irange pour cela:

for (int i : boost::irange(0, 100))
10
Angew

Pour la deuxième question - si Boost est trop lourd, vous pouvez toujours utiliser cette bibliothèque:

for(auto i : range(10, 15)) { cout << i << '\n'; } imprimera 10 11 12 13 14

for(auto i : range(20, 30, 2)) { cout << i << '\n'; } imprimera 20 22 24 26 28

Les doubles et autres types numériques sont également pris en charge.

Il a d'autres outils d'itération Pythonic et est en-tête uniquement.

4
onqtam

Vous pouvez faire ces deux choses avec Boost.Range: http://boost.org/libs/range

Par souci de brièveté (et pour pimenter un peu les choses, puisque boost::irange a déjà été démontré de manière isolée), voici un exemple de code montrant que ces fonctionnalités fonctionnent ensemble:

// boost::adaptors::indexed
// http://www.boost.org/doc/libs/master/libs/range/doc/html/range/reference/adaptors/reference/indexed.html
#include <boost/range/adaptor/indexed.hpp>

// boost::irange
// http://www.boost.org/doc/libs/master/libs/range/doc/html/range/reference/ranges/irange.html
#include <boost/range/irange.hpp>

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> input{11, 22, 33, 44, 55};
    std::cout << "boost::adaptors::indexed" << '\n';
    for (const auto & element : input | boost::adaptors::indexed())
    {
        std::cout << "Value = " << element.value()
                  << " Index = " << element.index()
                  << '\n';
    }

    endl(std::cout);

    std::cout << "boost::irange" << '\n';
    for (const auto & element : boost::irange(0, 5) | boost::adaptors::indexed(100))
    {
        std::cout << "Value = " << element.value()
                  << " Index = " << element.index()
                  << '\n';
    }

    return 0;
}

Exemple de sortie:

boost::adaptors::indexed
Value = 11 Index = 0
Value = 22 Index = 1
Value = 33 Index = 2
Value = 44 Index = 3
Value = 55 Index = 4

boost::irange
Value = 0 Index = 100
Value = 1 Index = 101
Value = 2 Index = 102
Value = 3 Index = 103
Value = 4 Index = 104
3
Matt

Si v est un vecteur (ou tout conteneur std contigu), alors

for(auto& x : v ) {
  size_t i = &x-v.data();
  x = i;
}

définira la ième entrée sur la valeur i.

Un itérateur de sortie qui compte est relativement facile à écrire. Boost en a un et a une gamme facile à générer appelée irange.

Extraire les index d'un conteneur est relativement facile. J'ai écrit une fonction appelée indexes qui peut prendre un conteneur ou une plage d'entiers, et génère des itérateurs de sortie aléatoires sur la plage en question.

Cela vous donne:

for (size_t i : indexes(v) ) {
  v[i] = i;
}

Il existe probablement une fonction de plage de conteneur à index équivalente dans Boost.

Si vous avez besoin des deux et que vous ne voulez pas faire le travail, vous pouvez écrire une fermeture à glissière.

for( auto z : Zip( v, indexes(v) ) ) {
  auto& x = std::get<0>(z);
  size_t i = std::get<1>(z);
  x = i;
}

Zip prend au moins deux plages (ou conteneurs) itérables et génère une vue de plage sur les n-uplets de iterator_traits<It>::references aux éléments.

Voici l'itérateur Boost Zip: http://www.boost.org/doc/libs/1_41_0/libs/iterator/doc/Zip_iterator.html - il existe probablement une plage Boost Zip qui gère la syntaxe comme celle-ci. au-dessus de la fonction Zip.

Pour la 2ème question:

Il y a un autre moyen, mais je ne voudrais pas l'utiliser ni le recommander . Cependant, pour configurer rapidement un test, vous pouvez écrire:

si vous ne voulez pas utiliser une bibliothèque et que vous pouvez très bien ne fournir que la limite supérieure de la plage, vous pouvez écrire:

for (auto i:vector<bool>(10)) {
    cout << "x";
}

Cela créera un vecteur booléen de taille 10, avec des valeurs non initialisées. En parcourant ces valeurs unitarisées en utilisant i (donc non utilisez i), il affichera 10 fois "x".

1
Stuck

Pour la deuxième question, si vous utilisez les dernières versions de Visual Studio, tapez 'if' then TabTab, et Tab pour remplir la valeur init, step-up et ainsi de suite.

0
Sheen