En C ++ 11, vous pouvez utiliser une plage basée for
, qui agit comme foreach
des autres langages. Cela fonctionne même avec des tableaux C simples:
int numbers[] = { 1, 2, 3, 4, 5 }; for (int& n : numbers) { n *= 2; }
Comment sait-il quand arrêter? Ne fonctionne-t-il qu’avec des tableaux statiques déclarés dans la même scope que dans for
? Comment utiliseriez-vous cela avec les tableaux dynamics?
Cela fonctionne pour toute expression dont le type est un tableau. Par exemple:
int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}}; for(int &n : *arraypointer) n *= 2; delete [] arraypointer;
Pour une explication plus détaillée, si le type de l’expression passée à droite de :
est un type tableau, la boucle effectue une itération de ptr
à ptr + size
( ptr
pointant vers le premier élément du tableau, size
étant le nombre d’éléments de le tableau).
Cela contraste avec les types définis par l’utilisateur, qui fonctionnent en regardant begin
et end
tant que membres si vous passez un object de classe ou (s’il n’y a pas de membre appelé ainsi) des fonctions non-membres. Ces fonctions donneront les iterators de début et de fin (pointant directement après le dernier élément et le début de la séquence respectivement).
Cette question clarifie pourquoi cette différence existe.
Je pense que la partie la plus importante de cette question est la suivante: comment C ++ sait-il quelle est la taille d’un tableau (du moins, je voulais le savoir quand j’ai trouvé cette question).
C ++ connaît la taille d’un tableau, car il fait partie de la définition du tableau – c’est le type de la variable. Un compilateur doit connaître le type.
Depuis C ++ 11, std::extent
peut être utilisé pour obtenir la taille d’un tableau:
int size1{ std::extent< char[5] >::value }; std::cout << "Array size: " << size1 << std::endl;
Bien sûr, cela n'a pas beaucoup de sens, car vous devez fournir explicitement la taille dans la première ligne, que vous obtenez ensuite dans la deuxième ligne. Mais vous pouvez aussi utiliser decltype
et ensuite ça devient plus intéressant:
char v[] { 'A', 'B', 'C', 'D' }; int size2{ std::extent< decltype(v) >::value }; std::cout << "Array size: " << size2 << std::endl;
Selon la dernière version de travail C ++ (n3376), l’instruction rangeed est la suivante:
{ auto && __range = range-init; for (auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin) { for-range-declaration = *__begin; statement } }
Donc, il sait comment arrêter de la même manière que for
iterators classiques.
Je pense que vous pourriez chercher quelque chose comme ce qui suit pour fournir un moyen d’utiliser la syntaxe ci-dessus avec des tableaux composés uniquement d’un pointeur et d’une taille (tableaux dynamics):
template class Range { public: Range(T* collection, size_t size) : mCollection(collection), mSize(size) { } T* begin() { return &mCollection[0]; } T* end () { return &mCollection[mSize]; } private: T* mCollection; size_t mSize; };
Ce modèle de classe peut ensuite être utilisé pour créer une plage sur laquelle vous pouvez effectuer une itération à l’aide de la nouvelle syntaxe. Je l’utilise pour parcourir tous les objects d’animation d’une scène imscope à l’aide d’une bibliothèque qui renvoie uniquement un pointeur vers un tableau et une taille en tant que valeurs séparées.
for ( auto pAnimation : Range(pScene->mAnimations, pScene->mNumAnimations) ) { // Do something with each pAnimation instance here }
A mon avis, cette syntaxe est beaucoup plus claire que ce que vous obtiendriez en utilisant std::for_each
ou une plaine for
boucle.
Il sait quand arrêter car il connaît les limites des tableaux statiques.
Je ne suis pas sûr de ce que vous entendez par “tableaux dynamics”, dans tous les cas, si vous ne parcourez pas les tableaux statiques de manière informelle, le compilateur recherche les noms begin
et end
dans la scope de l’object parcouru, ou recherche pour begin(range)
et end(range)
utilisant une recherche dépendante des arguments et les utilise comme iterators.
Pour plus d’informations, dans la norme C ++ 11 (ou son ébauche publique), “6.5.4 L’énoncé basé for
intervalles”, p.
Comment fonctionne la gamme basée sur le travail pour les tableaux simples?
Est-ce que cela doit se lire comme suit: ” Dites-moi ce que fait un jeu à distance (avec des tableaux)? ”
Je vais répondre en supposant que – Prenez l’exemple suivant en utilisant des tableaux nesteds:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (auto &pl : ia)
Version texte:
ia
est un tableau de tableaux (“tableau nested”), contenant [3]
tableaux, chacun contenant [4]
valeurs. L’exemple ci-dessus boucle ia
par son ‘range’ primaire ( [3]
), et donc les boucles [3]
fois. Chaque boucle produit l’une des [3]
premières valeurs de ia
commençant par la première et se terminant par la dernière – Un tableau contenant des valeurs [4]
.
pl
est égal à {1,2,3,4}
– Un tableau pl
est égal à {5,6,7,8}
– Un tableau pl
est égal à {9,10,11,12}
– Un tableau Avant d’expliquer le processus, voici quelques rappels amicaux sur les tableaux:
pl
doit être une référence car nous ne pouvons pas copier les tableaux n
est le nombre en question, alors ia[n]
est identique à *(ia+n)
(Nous sums en train de déréférencer l’adresse vers l’avant), et ia+n
est identique à &ia[n]
(nous obtenons l’adresse de cette entrée dans le tableau). Voici ce qui se passe:
pl
est défini comme une référence à ia[n]
, n
égal au compte de boucle courant à partir de 0. Donc, pl
est ia[0]
au premier tour, au second ia[1]
, et bientôt. Il récupère la valeur via une itération. ia+n
est inférieur à end(ia)
. … Et c’est à peu près tout.
C’est vraiment une façon simple d’écrire ceci :
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int n = 0; n != 3; ++n) auto &pl = ia[n];
Si votre tableau n’est pas nested, alors ce processus devient un peu plus simple car une référence n’est pas nécessaire, car la valeur itérée n’est pas un tableau mais une valeur «normale»:
int ib[3] = {1,2,3}; // short for (auto pl : ib) cout << pl; // long for (int n = 0; n != 3; ++n) cout << ib[n];
Quelques informations supplémentaires
Et si nous ne voulions pas utiliser le mot auto
clé auto
lors de la création de pl
? A quoi cela ressemblerait-il?
Dans l'exemple suivant, pl
fait référence à un array of four integers
. Sur chaque boucle, pl
reçoit la valeur ia[n]
:
int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; for (int (&pl)[4] : ia)
Et ... Voilà comment cela fonctionne, avec des informations supplémentaires pour éliminer toute confusion. C'est juste un raccourci for
boucle qui compte automatiquement pour vous, mais ne permet pas de récupérer la boucle actuelle sans la faire manuellement.