J'ai senti qu'aucune des réponses ici n'explique pourquoi j'aime les itérateurs comme concept général par rapport à l'indexation dans des conteneurs. Notez que la plupart de mon expérience avec les itérateurs ne vient pas réellement de C ++ mais de langages de programmation de niveau supérieur comme Python.
L'interface de l'itérateur impose moins d'exigences aux consommateurs de votre fonction, ce qui permet aux consommateurs d'en faire plus avec elle.
Si tout ce dont vous avez besoin est de pouvoir effectuer une itération directe, le développeur n'est pas limité à l'utilisation de conteneurs indexables - il peut utiliser n'importe quelle implémentation de classe operator++(T&)
, operator*(T)
et operator!=(const &T, const &T)
.
#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
for (auto current = begin; current != end; ++current) {
std::cout << *current << "\n";
}
}
// elsewhere...
printAll(myVector.begin(), myVector.end());
Votre algorithme fonctionne pour le cas où vous en avez besoin - itération sur un vecteur - mais il peut également être utile pour les applications que vous n'anticipez pas nécessairement:
#include <random>
class RandomIterator
{
private:
std::mt19937 random;
std::uint_fast32_t current;
std::uint_fast32_t floor;
std::uint_fast32_t ceil;
public:
RandomIterator(
std::uint_fast32_t floor = 0,
std::uint_fast32_t ceil = UINT_FAST32_MAX,
std::uint_fast32_t seed = std::mt19937::default_seed
) :
floor(floor),
ceil(ceil)
{
random.seed(seed);
++(*this);
}
RandomIterator& operator++()
{
current = floor + (random() % (ceil - floor));
}
std::uint_fast32_t operator*() const
{
return current;
}
bool operator!=(const RandomIterator &that) const
{
return current != that.current;
}
};
int main()
{
// roll a 1d6 until we get a 6 and print the results
RandomIterator firstRandom(1, 7, std::random_device()());
RandomIterator secondRandom(6, 7);
printAll(firstRandom, secondRandom);
return 0;
}
Tenter d'implémenter un opérateur entre crochets qui fait quelque chose de similaire à cet itérateur serait artificiel, tandis que l'implémentation de l'itérateur est relativement simple. L'opérateur entre crochets a également des implications sur les capacités de votre classe - que vous pouvez indexer à n'importe quel point arbitraire - qui peuvent être difficiles ou inefficaces à implémenter.
Les itérateurs se prêtent également à la décoration . Les utilisateurs peuvent écrire des itérateurs qui prennent un itérateur dans leur constructeur et étendent ses fonctionnalités:
template<class InputIterator, typename T>
class FilterIterator
{
private:
InputIterator internalIterator;
public:
FilterIterator(const InputIterator &iterator):
internalIterator(iterator)
{
}
virtual bool condition(T) = 0;
FilterIterator<InputIterator, T>& operator++()
{
do {
++(internalIterator);
} while (!condition(*internalIterator));
return *this;
}
T operator*()
{
// Needed for the first result
if (!condition(*internalIterator))
++(*this);
return *internalIterator;
}
virtual bool operator!=(const FilterIterator& that) const
{
return internalIterator != that.internalIterator;
}
};
template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
EvenIterator(const InputIterator &internalIterator) :
FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
{
}
bool condition(std::uint_fast32_t n)
{
return !(n % 2);
}
};
int main()
{
// Rolls a d20 until a 20 is rolled and discards odd rolls
EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
printAll(firstRandom, secondRandom);
return 0;
}
Bien que ces jouets puissent sembler banals, il n'est pas difficile d'imaginer utiliser des itérateurs et des décorateurs d'itérateurs pour faire des choses puissantes avec une interface simple - décorer un itérateur en avant uniquement des résultats de la base de données avec un itérateur qui construit un objet modèle à partir d'un seul résultat, par exemple . Ces modèles permettent une itération efficace en mémoire d'ensembles infinis et, avec un filtre comme celui que j'ai écrit ci-dessus, une évaluation potentiellement paresseuse des résultats.
Une partie de la puissance des modèles C ++ est votre interface d'itérateur, lorsqu'elle est appliquée à des tableaux C de longueur fixe, se désintègre en une arithmétique de pointeur simple et efficace , ce qui en fait une abstraction vraiment sans coût.
some_iterator++
à++some_iterator
. Le post-incrément crée un itérateur temporaire inutile.