15 votes

Pour les itérateurs d'entrée, pourquoi a == b n'implique pas ++a == ++b ?

Le §24.1.1/3 de la norme C++03 se lit comme suit,

Pour les itérateurs d'entrée, a == b n'implique pas ++a == ++b. ( L'égalité ne garantit la propriété de substitution ou transparence référentielle. ) Algorithmes sur les itérateurs d'entrée ne devraient jamais jamais essayer de passer deux fois par le même même itérateur deux fois. Ils doivent être uniques passage. Le type de valeur T n'est pas obligatoirement un type Assignable (23.1). Ces algorithmes peuvent être utilisés avec istreams comme source des données d'entrée par l'intermédiaire de la classe istream_iterator.

Je n'ai pas compris le texte en gras dans la citation ci-dessus. Quelqu'un peut-il m'aider à le comprendre ?

Par ailleurs, que signifie la déclaration suivante (texte en italique dans la citation ci-dessus) ? Comment est-elle liée à a==b y ++a==++b des expressions ?

L'égalité n'est pas garantit pas la propriété de substitution ou la transparence référentielle.

10voto

Steve Jessop Points 166970

Pour les itérateurs d'entrée, l'incrémentation d'un itérateur invalide les copies du même itérateur.

Donc :

auto a = istream_iterator<whatever>(something);
auto b = a;
a == b; // true
++a;    // b is now invalid
++b;    // undefined behavior, I think, but in any case not guaranteed to
        // result in anything sensible.

Alors certainement ++a == ++b n'est pas garanti. En effet, a == b n'implique pas ++a == ++b .

Je pense que la "propriété de substitution" signifie "tout ce que vous faites avec la valeur de a a le même résultat que de faire la même chose avec la valeur de b "Il y a plusieurs versions de la substitution auxquelles il peut se référer, mais quelque chose de ce genre. Je pense que dans ce contexte, cela doit signifier " plus tard en faisant de même avec b ", car si a == b et que je n'ai pas encore fait quelque chose d'invalide, alors ça n'a pas d'importance laquelle des a y b J'utilise, ils se réfèrent au même point du cours d'eau. Mais lorsque j'incrémente, je dois en choisir un et perdre l'autre, d'où la difficulté avec ++a == ++b .

La "transparence référentielle" signifie que différents objets sont indépendants, c'est-à-dire qu'ils ne sont pas des références/pointeurs ou des alias les uns des autres. En combinaison avec la "propriété de substitution", cela signifie :

Plus tard ? Il n'y a pas plus tôt ou plus tard puisque les opérations n'ont pas d'effets secondaires globaux. Si vous ne pouvez pas substituer "plus tard", alors vous ne pouvez pas substituer

Les itérateurs d'entrée de la même séquence font généralement référence aux mêmes "données réelles", comme un gestionnaire de fichier ou autre, qui contient lui-même un état mutable. Puisque a y b font référence au même handle de fichier, et leur valeur dépend de son état, vous n'avez pas de transparence référentielle. Ce manque est pourquoi La substitution échoue.

Les itérateurs avant sont généralement également font référence aux mêmes données sous-jacentes (comme un conteneur), mais tant que vous les utilisez en lecture seule (et que vous ne modifiez pas autrement le conteneur), ils ne trahissent pas ce fait, du moins pas avant que vous ne commenciez à comparer les adresses des valeurs qu'ils renvoient. Ils ont donc un type limité de transparence référentielle. de leur propre valeur que les itérateurs d'entrée n'ont pas. Ils sont toujours des références eux-mêmes, donc les choses auxquelles ils se réfèrent sont toujours aliasées.

6voto

Konrad Rudolph Points 231505

Comme le dit l'explication : "Ils devraient être des algorithmes à passage unique."

Le fait est qu'un itérateur sur un flux d'entrée représente un état transitoire. Une fois que l'itérateur est modifié, cet état n'existe plus ; tous les autres itérateurs représentant cet état sont invalidés : une fois que vous incrémentez a l'itérateur b devient invalide.

4voto

Matthieu M. Points 101624

Les propriétés visées sont les suivantes :

Propriété de substitution

Pour toute quantité a et b et toute expression F(x), si a = b, alors F(a) = F(b) (si les deux côtés ont un sens, c'est-à-dire s'ils sont bien formés).

_Transparence référentielle_

De manière informelle, cela signifie qu'il n'y a pas de différence entre une valeur et une référence à cette valeur (d'où l'origine du terme).

En programmation impérative, c'est un concept difficile à comprendre, car nous sommes habitués à modifier nos variables. Rick Hickey (à l'origine de Clojure) donne une belle conférence sur la distinction entre l'identité et l'État qui pourrait vous aider. L'essentiel est qu'une variable est une identité. À tout moment, une identité fait référence à un état. Un état ne change jamais, mais une identité peut être modifiée pour faire référence à un autre état.

Itérateurs d'entrée

En propriété de substitution la violation est "évidente" ici, si nous définissons F(x) dans le texte ci-dessus pour signifier ++x alors nous avons que si les itérateurs d'entrée vérifiaient la propriété de substitution, ce qui suit s'appliquerait a == b => ++a == ++b .

Ce n'est cependant pas vrai, car l'incrémentation d'un itérateur d'entrée peut invalider tous les autres itérateurs d'entrée de la même source. Extrait du tableau 107 du document n3290 (page 831, juste au-dessus du paragraphe que vous avez cité) :

++r

pre : r est déréférençable.

post : r est déréférençable ou r est past-the-end.

post : toute copie de la valeur précédente de r n'a plus besoin d'être déréférencée ou d'être dans le domaine de ==. déréférencement ou qu'elles soient dans le domaine de ==.

C'est-à-dire que lorsque nous effectuons ++a entonces b peut devenir invalide, et donc ++b lui-même sera un comportement indéfini.

C'est une violation directe de ++a == ++b Par conséquent, la propriété de substitution ne tient pas.

En transparence référentielle est un peu plus évident ici. Si les itérateurs d'entrée étaient référentiellement transparents, cela signifierait qu'ils seraient indifférenciés de la valeur qu'ils pointent. Il est clair que ce n'est pas le cas, car en appliquant la méthode ++ n'incrémente pas la valeur, mais l'itérateur.

3voto

James Kanze Points 96599

Les itérateurs d'entrée définissent une séquence qui ne peut être lue qu'une seule fois. d'un clavier ou d'un tuyau en serait un bon exemple. L'incrémentation d'un istream_iterator signifie effectivement lire plus loin dans le istream , l'extraction de caractères, donc d'autres istream_iterator sur le même flux ne seront plus valables en ce qui concerne leur position. Imaginez un flux de personnages "abcdefg..." (l'alphabet, en somme), avec deux istream_iterator<char> pointant vers le 'a' . L'incrémentation de l'un de ces l'un d'eux entraînera la 'b' à lire depuis le flux, et aucune autre itérateur ne pourra jamais le voir.

1voto

Bo Persson Points 42821

Considérez qu'un itérateur d'entrée pourrait être connecté à un flux lisant à partir du clavier. Incrémenter l'itérateur signifie lire le prochain caractère.

De même, incrémenter une copie de l'itérateur ne signifie pas lire le même caractère.

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