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;
}
50
c++
demandé sur sfjac 2017-12-16 23:23:17

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.

39
répondu Stephan Lechner 2017-12-17 09:21:27

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:

  1. lors de la mutation des éléments de la séquence, une valeur n'aide pas vraiment.
  2. dans la plupart des cas, copier le les valeurs sont coûteuses et garder une référence est plus efficace.
  3. 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) et T 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é.

33
répondu Dietmar Kühl 2017-12-17 21:54:57

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++.

1
répondu M4HdYaR 2017-12-17 09:25:41

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.

0
répondu Mehrdad 2017-12-17 13:07:51