Utilisation de la variable déclarée dans une boucle for basée sur une plage
Dans l'exemple ci-dessous, i
a une portée de fonction. Mais il semble que je ne puisse pas utiliser i
dans la seconde boucle for. Pourquoi for (i : v1)
ne fonctionne-t-il pas, mais for (int i : v1)
fonctionne-t-il?
#include<iostream>
#include<string>
#include<vector>
int main()
{
std::vector<int> v1;
int i;
while(std::cin>>i)
{
v1.push_back(i);
}
for(i : v1) //for (int i:v1) works
std::cout<<i<<"t";
cout<<std::endl;
return 0;
}
4 réponses
C'est un problème syntaxique qu'une boucle for
basée sur une plage nécessite une déclaration d'une variable nommée , c'est-à-dire qu'elle nécessite un spécificateur de type (cf, par exemple, cppreference.com):
Pour (range_declaration : range_expression ) loop_statement
Range_declaration - unedéclaration d'une variable nommée, dont le type est le type de l'élément de la séquence représentée par range_expression, ou une référence à ce type. Utilise souvent auto spécificateur pour la déduction automatique de type
En fait, je ne sais pas pourquoi votre question a été downvoted; je trouve votre hypothèse assez correcte; juste la syntaxe C++ a décidé de la définir d'une autre manière.
Le range-based for
est spécifiquement destiné à remplacer les boucles similaires à ce qui suit (c'est un cas un peu simpliste; range-based for
, en particulier la version C++17, est plus général que l'exemple):
for (auto it = range.begin(), end = range.end(); it != end; ++it) {
use(*it);
}
Dans la majorité des cas, n'utilisera pas les valeurs aux différents emplacements mais utilisera plutôt l'élément à l'emplacement lui-même:
- lors de la mutation des éléments de la séquence, une valeur n'aide pas vraiment.
- dans la plupart des cas, copier le les valeurs sont coûteuses et garder une référence est plus efficace.
- Il y a même des cas où les objets ne peuvent pas être copiés pour commencer.
En conséquence, les concepteurs de range-based for
ont décidé que les références doivent absolument être prises en charge. Dans le même temps, il était prévu d'utiliser une règle de réécriture raisonnablement simpliste pour un for
basé sur une plage. La règle qui est codifiée dans la norme est ceci:
for (<range-decl>: <range>) { <body> }
Est équivalent à
{
auto&& range = <range>; // keep the range alive!
auto it = begin(range); // actually, reality is bit more complicated
auto end = end(range); // actually, reality is a bit more complicated
for (; it != end; ++it) {
<range-decl> = *it; // this is the rewrite causing your issue
<body>
}
}
En particulier, le en déduire que <range-decl>
est un déclaration de, plutôt que de simplement nommer une variable. La raison de cette exigence est que généralement l'entité utilisée devant le :
est une référence. Cependant, les références ne peuvent pas être rebondies. Cependant, à chaque itération de la boucle une nouvelle référence peut être utilisé.
En principe, la règle de réécriture peut fonctionner avec l'utilisation d'affectations Si <range-decl>
n'est pas une déclaration mais plutôt une lvalue. Cela donnerait sa propre part d'Impair comportements:
- Il y aurait une différence entre
for (T const& x: range)
etT const& x = 0; for (x: range)
: le premier fonctionne alors que le second est une erreur. - Si lvalue est une référence à un objet situé quelque part (
T& x = get_reference(); for (x: range) {...}
), la boucle affecterait automatiquement toutes les valeurs d'une plage à un objet situé quelque part. Normalement, les objets sont situés sur la pile ou dans la plage source (lorsque la variable est déclarée comme référence).
Il était considéré plus raisonnable de ne permettre initialisations que la prise en charge de l'initialisation ou des affectations en fonction de la façon dont la variable est déclarée. En regardant l'historique des révisions des propositions ( N2930 et prédécesseurs) ne donne pas lieu à une discussion, mais je me souviens vaguement que ce point a été discuté.
Lorsque vous utilisez des boucles basées sur des plages, vous avez besoin d'une déclaration après l'ouverture des parenthèses, pas seulement d'une variable. La syntaxe correcte est:
for ( declaration : range ) statement;
, Vous pouvez voir ce lien pour plus d'informations.
Dans votre exemple: lorsque vous déclarez i
avant votre while
boucle, alors vous pouvez l'utiliser dans tous les main
la fonction et la portée de la main
function. Vous pouvez l'utiliser dans ce corps for
. Lorsque vous utilisez la variable i
dans votre plage for
, vous ne le déclarez pas, parce que vous l'avez déjà déclaré ci-dessus, donc cela vous donnera une erreur et ce n'est pas correct avec la syntaxe c++.
, Mais lorsque vous tapez int
avant i
votre for
parenthèse ensuite, vous devez déclarer une variable avec le nom de i
, mais seulement pour votre for
boucle et puis c'est OK avec la syntaxe C++.
La justification est très probablement parce que cela invoquerait copy-assignments sur la variable, ce qui deviendrait une source potentielle de grande inefficacité et en pratique ne serait presque jamais l'intention... si le type prend en charge l'assignation de copie.
Donc, ils ont probablement pensé qu'il est préférable d'interdire cela.