web-dev-qa-db-fra.com

Quel est le moyen le plus efficace d’obtenir l’index d’un itérateur d’un std :: vector?

Je parcourt un vecteur et ai besoin de l'index sur lequel l'itérateur pointe actuellement. Autant que je sache, cela peut être fait de deux manières:

  • it - vec.begin()
  • std::distance(vec.begin(), it)

Quels sont les avantages et les inconvénients de ces méthodes?

398
cairol

Je préférerais it - vec.begin() précisément pour la raison opposée donnée par Naveen: ainsi ne compilerait pas si vous modifiez le vecteur en liste. Si vous faites cela à chaque itération, vous pourriez facilement transformer un algorithme O(n) en un algorithme O (n ^ 2).

Une autre option, si vous ne sautez pas dans le conteneur lors de l'itération, consisterait à conserver l'index en tant que deuxième compteur de boucle.

Remarque: it est le nom usuel d'un itérateur de conteneur, std::container_type::iterator it;.

507
UncleBens

Je préférerais std::distance(vec.begin(), it) car cela me permettrait de changer de conteneur sans modifier le code. Par exemple, si vous décidez d'utiliser std::list au lieu de std::vector qui ne fournit pas d'itérateur à accès aléatoire, votre code sera toujours compilé. Puisque std :: distance sélectionne la méthode optimale en fonction des caractéristiques de l’itérateur, vous ne subirez aucune dégradation des performances.

124
Naveen

Comme UncleBens et Naveen l'ont montré, il existe de bonnes raisons pour les deux. Laquelle est "meilleure" dépend du comportement que vous souhaitez: voulez-vous garantir un comportement à temps constant ou voulez-vous qu'il retombe en temps linéaire lorsque cela est nécessaire?

it - vec.begin() prend un temps constant, mais le operator - n'est défini que sur des itérateurs à accès aléatoire, de sorte que le code ne sera pas compilé du tout avec des itérateurs de liste, par exemple.

std::distance(vec.begin(), it) fonctionne pour tous les types d'itérateurs, mais ne sera une opération à temps constant que s'il est utilisé sur des itérateurs à accès aléatoire.

Ni l'un ni l'autre n'est "meilleur". Utilisez celui qui fait ce dont vous avez besoin.

71
jalf

J'aime celui-ci: it - vec.begin(), car il me dit clairement "distance du début". Avec les itérateurs, nous avons l'habitude de penser en termes d'arithmétique, donc le signe - est l'indicateur le plus clair ici.

11
Eli Bendersky

Si votre algorithme est déjà limité/codé en dur pour utiliser uniquement std::vector::iterator et std::vector::iterator, la méthode que vous utiliserez n'a pas vraiment d'importance. Votre algorithme est déjà concrétisé au-delà du point où choisir l’un des autres peut faire la différence. Ils font tous les deux exactement la même chose. C'est juste une question de préférence personnelle. Je voudrais personnellement utiliser la soustraction explicite.

Si, en revanche, vous souhaitez conserver un degré de généralité plus élevé dans votre algorithme, notamment pour permettre la possibilité qu’il soit appliqué à un autre type d’itérateur, la meilleure méthode dépend de votre intention. . Cela dépend du degré de restriction que vous souhaitez avoir en ce qui concerne le type d'itérateur qui peut être utilisé ici.

  • Si vous utilisez la soustraction explicite, votre algorithme sera limité à une classe d'itérateurs plutôt étroite: les itérateurs à accès aléatoire. (C'est ce que vous obtenez maintenant de std::vector)

  • Si vous utilisez distance, votre algorithme supportera une classe beaucoup plus large d'itérateurs: les itérateurs d'entrée.

Bien entendu, le calcul de distance pour les itérateurs à accès non aléatoire est généralement une opération inefficace (alors que, encore une fois, pour les itérateurs à accès aléatoire, il est aussi efficace que la soustraction). C'est à vous de décider si votre algorithme logique pour les itérateurs à accès non aléatoire, en termes d'efficacité. Si la perte d'efficacité qui en résulte est dévastatrice au point de rendre votre algorithme complètement inutile, vous devriez alors vous en tenir à la soustraction, interdisant ainsi les utilisations inefficaces et obligeant l'utilisateur à rechercher des solutions alternatives pour d'autres types d'itérateurs. Si l'efficacité avec les itérateurs à accès non aléatoire est toujours dans la plage utilisable, vous devez alors utiliser distance et documenter le fait que l'algorithme fonctionne mieux avec les itérateurs à accès aléatoire.

7
AnT

Selon http://www.cplusplus.com/reference/std/iterator/distance/ , puisque vec.begin() est un accès aléatoire itérateur, la méthode de distance utilise l'opérateur -.

La réponse est donc, du point de vue des performances, identique, mais peut-être que l’utilisation de distance() est plus facile à comprendre si quelqu'un devait lire et comprendre votre code.

4
Stéphane

J'utiliserais uniquement la variante - pour std::vector - le sens est clair, et la simplicité de l'opération (qui n'est autre qu'une soustraction de pointeur) est exprimée par la syntaxe (distance, de l’autre côté, sonne comme un pythagore en première lecture, n’est-ce pas?). Comme le souligne UncleBen, - agit également comme une assertion statique dans le cas où vector est modifié par inadvertance en list.

De plus, je pense que c'est beaucoup plus courant - pas de chiffres pour le prouver, cependant. L'argument principal: it - vec.begin() est plus court dans le code source - moins de travail de frappe, moins d'espace utilisé. Comme il est clair que la bonne réponse à votre question se résume à une question de goût, ceci peut aussi être un argument valable.

3
Alexander Gessler