2
votes

L'itérateur d'entrée peut être lu à plusieurs reprises tandis que l'itérateur de sortie ne peut être écrit qu'une seule fois?

Je lisais la 4e édition de The C ++ Programming Language par Bjarne Stroustrup. Dans le chapitre de l'itérateur (chapitre 31.1.2), il est dit:

Itérateur d'entrée: nous pouvons effectuer une itération vers l'avant en utilisant ++ et lire chaque élément ( à plusieurs reprises ) en utilisant *.

Itérateur de sortie: nous pouvons effectuer une itération vers l'avant en utilisant ++ et écrire un élément une seule fois en utilisant *.

J'ai fait de nombreuses recherches pour savoir si l'itérateur d'entrée peut être lu une seule fois ou à plusieurs reprises, par exemple: http://www.cplusplus.com/reference/iterator/InputIterator/ https://www.geeksforgeeks.org/input-iterators-in-cpp/ < / a>

et la plupart suggèrent que l'itérateur d'entrée ne peut être lu qu'une seule fois. Mais pourquoi l'auteur dit-il à plusieurs reprises pour l'itérateur d'entrée? Est-ce correct? Et si tel est le cas, pourquoi l'itérateur d'entrée peut être lu à plusieurs reprises, mais l'itérateur de sortie ne peut être écrit qu'une seule fois. J'ai toujours pensé que les itérateurs d'entrée et de sortie étaient complètement opposés.

Merci à tous!


0 commentaires

3 Réponses :


3
votes

Le livre est correct; et les sources contradictoires ne le sont pas. Il ne semble y avoir aucune règle interdisant la lecture d'un objet plus d'une fois en passant par un itérateur d'entrée.

Les autres sources peuvent être confondues par une autre limitation similaire qui est qu'une fois que l'itérateur d'entrée a été incrémenté, toutes les copies de l'itérateur précédent sont invalidées et ne peuvent donc plus être indirectes. Cette limitation est partagée par les itérateurs de sortie. Par exemple:

 *outputIt = value;
  ++outputIt;
 *outputIt = value; // OK
 *outputIt = value; // Not OK

Le livre est également correct que l'itérateur de sortie a la limitation:

value = *inputIt;
value = *inputIt; // OK
copyIt = inputIt;
++inputIt;
value = *copyIt;  // Not OK

J'ai toujours pensé que les itérateurs d'entrée et de sortie étaient complètement opposés.

De nombreux itérateurs de sortie sont également des itérateurs d'entrée, donc "opposé" n'est pas exactement très descriptif. Ce sont des ensembles d'exigences qui se chevauchent partiellement. Un itérateur peut répondre aux deux ensembles d'exigences.

Si nous avons * outputIt = 1; alors * outputIt = 2; ne sommes-nous pas simplement en train d'assigner deux fois la même * sortie?

Oui; Et c'est quelque chose que les itérateurs de sortie ne sont pas obligés de prendre en charge.

Prenons par exemple un itérateur de sortie qui envoie des paquets sur Internet. Vous avez écrit un paquet, et il a été envoyé sur Internet et reçu par une autre machine. Vous ne pouvez pas voyager dans le temps et décider que le paquet qui a été envoyé est différent. Vous devez passer au paquet suivant et l'envoyer à la place.


6 commentaires

Merci pour votre réponse. Que diriez-vous de l'itérateur de sortie. par exemple écrire * outputIt = value; deux fois comme votre exemple n'est pas OK / ne compilera pas?


@ user9607441 * outputIt = valeur; * outputIt = value; ne serait pas OK. Il compilerait, car il est bien formé, mais le comportement n'a pas besoin d'être bien défini (cela peut l'être, dépend de la façon dont l'itérateur a été défini).


@ user9607441 Il est important de comprendre qu'en C ++, quelque chose peut à la fois se compiler et ne pas être OK en même temps.


Merci à tous ... Je ne sais toujours pas pourquoi le deuxième * outputIt = value; n'est pas OK? Si nous avons * outputIt = 1; alors * outputIt = 2; ne sommes-nous pas simplement en train d'assigner deux fois la même sortie *? Qu'est-ce qui nous empêche de faire cela et quels types de «comportements indéfinis» cela causera-t-il?


@ user9607441 J'ai ajouté un exemple pour décrire pourquoi ce n'est pas OK.


@eerorika merci !! Vous avez été vraiment utile! Je pense que je l'ai enfin compris!



2
votes

La citation de Bjarne est correcte. Si vous avez un itérateur d'entrée, vous pouvez faire * iterator autant de fois que vous le souhaitez. Si vous avez un itérateur de sortie, vous ne pouvez faire * iterator qu'une seule fois.

Ce qu'ils ont tous les deux en commun, c'est qu'ils ne peuvent être utilisés que dans des algorithmes en un seul passage. Une fois que vous incrémentez un itérateur d'entrée ou de sortie, un itérateur vers une position précédente n'est plus obligé d'être déréférencable

Cela signifie que dans

while (iterator != end)
{
    something = *iterator;
    ++iterator;
}

iterator doit être un itérateur d'entrée puisque nous le déréférencons deux fois à chaque itération. D'autre part

while (iterator != end)
{
    if (*iterator == some_value)
        something = *iterator;
    ++iterator;
}

fonctionne à la fois pour les itérateurs d'entrée et de sortie car nous ne faisons qu'un seul déréférencement.


4 commentaires

Merci pour votre réponse! mais pourquoi l'itérateur de sortie ne peut être déréférencé qu'une seule fois alors que l'itérateur d'entrée peut être déréférencé plusieurs fois avant ++ sans provoquer de "comportements non définis"


@ user9607441 C'est exactement ce que la norme permet. Une implémentation peut permettre à un itérateur de sortie d'être déréférencé plusieurs fois, mais la norme n'impose pas cela à l'implémentation. Pourquoi exactement, je ne suis pas sûr, mais cela est probablement lié à l'écriture dans les flux de sortie où ils ne s'attendent à obtenir qu'une seule valeur par élément.


Merci @NathanOliver! Je pense que je le comprends maintenant. Ainsi, la norme dit explicitement que l'itérateur de sortie ne peut être lu qu'une seule fois mais ne met pas cette restriction sur l'itérateur d'entrée?


@ user9607441 Oui. La norme dit que vous ne pouvez déréférencer en toute sécurité un itérateur de sortie qu'une seule fois. Il ne met pas cette mise en garde sur un itérateur d'entrée, vous êtes donc autorisé à déréférencer autant de fois que vous le souhaitez.



1
votes

Vous pouvez lire un itérateur d'entrée autant de fois que vous le souhaitez. Cela vient de l'exigence que " (void) * a, * a est équivalent à * a " [input.iterators], voir le tableau.

Vous ne pouvez écrire qu'une seule fois via un itérateur de sortie. Pour * r = o , "Après cette opération, r n'est pas obligé d'être déréférencable". [output.iterators], voir le tableau. Après avoir incrémenté r , vous avez un nouvel itérateur et vous pouvez à nouveau l'assigner une fois.

Une fois que vous avez combiné ces deux éléments dans un itérateur avant , la restriction sur les attributions multiples via le même itérateur disparaît.


1 commentaires

Merci @Pete Becker! Je viens de trouver vos références dans la documentation standard