51 votes

Utilisation d'une 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. Pourquoi for (i : v1) ne fonctionne pas, mais for (int i : v1) fonctionne?

 #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;
}
 

39voto

Stephan Lechner Points 29375

C'est un problème de syntaxe qu'une gamme à base for boucle nécessite une déclaration d'une variable nommée, c'est à dire qu'il nécessite un spécificateur de type (cf, par exemple, cppreference.com):

pour ( range_declaration : range_expression ) loop_statement

range_declaration - une dé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. Souvent utilise l'auto spécificateur de type automatique déduction

En fait, je ne sais pas pourquoi votre question a obtenu downvoted; je trouve ton hypothèse tout à fait OK; la syntaxe C++ a décidé de définir d'une autre manière.

34voto

Dietmar Kühl Points 70604

La gamme de base for est spécifiquement destiné à remplacer les boucles de semblable à la suivante (c'est un peu simpliste cas; basés sur la plage, for, en particulier le C++17 version, 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, de ne pas utiliser les valeurs à différents endroits, mais plutôt d'utiliser l'élément à l'emplacement lui-même:

  1. Lors de la mutation d'éléments dans la séquence d'une valeur n'aide pas vraiment.
  2. Dans la plupart des cas, la copie de valeurs est cher 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 gamme à base for décidé que les références absolument être pris en charge. Dans le même temps, il était prévu d'utiliser une assez simpliste réécrire la règle pour une gamme à base for. La règle qui est codifiée dans la norme ce:

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, l'implication est qu' <range-decl> est une déclaration plutôt que de simplement nommer une variable. La raison pour cela est que, généralement, l'entité à l'avant de l' : est une référence. Toutefois, les références ne peuvent pas être reprise. Cependant, à chaque itération de la boucle une nouvelle référence peut être utilisé.

En principe, la règle de réécriture pourrait, avec l'aide des affectations si l' <range-decl> n'est pas une déclaration mais plutôt une lvalue. Que produirait sa propre part de comportements bizarres:

  • Il y aurait une différence entre for (T const& x: range) et T const& x = 0; for (x: range): les anciens travaux, tandis que le dernier est une erreur.
  • Si la lvalue est une référence à un objet situé quelque part (T& x = get_reference(); for (x: range) {...}) la boucle serait d'attribuer automatiquement toutes les valeurs dans une plage d'un objet situé quelque part. Normalement, les objets sont situés soit sur la pile ou dans la plage source (lorsque la variable est déclarée comme une référence).

Il a été d'étudier plus raisonnable d'autoriser uniquement les initialisations de l'appui de l'initialisation ou les affectations en fonction de la façon dont la variable est déclarée. En regardant l'historique de la révision des propositions (N2930 et prédécesseurs) ne donne pas une discussion, mais j'ai vaguement rappeler que sat point a été discuté.

1voto

M4HdYaR Points 683

Lorsque vous êtes à l'aide de gamme à base de boucles vous avez besoin d'une déclaration après l'ouverture de parenthèses, non seulement 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 l'ensemble de l' main de la fonction et la portée de c'est l' main fonction. Vous pouvez l'utiliser dans la for du corps. Lorsque vous utilisez l' i variable for gamme alors vous n'avez pas le déclarer, car vous avez déjà déclaré ci-dessus, il vous donnera une erreur et il n'est pas correct avec la syntaxe C++.

Mais lorsque vous tapez int avant l' i votre for parenthèse ensuite, vous devez déclarer une variable avec le nom de l' i, mais seulement pour votre for boucle et puis c'est OK avec la syntaxe C++.

0voto

Mehrdad Points 70493

La raison en est très probablement parce que cela invoquerait des assignations de copie sur la variable, ce qui deviendrait une source potentielle de grande inefficacité et ne serait pratiquement jamais l'intention… si le type supporte la moindre affectation de copie.
Donc, ils ont probablement pensé qu'il était préférable d'interdire cela.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X