Sentí que ninguna de las respuestas aquí explica por qué me gustan los iteradores como concepto general sobre la indexación en contenedores. Tenga en cuenta que la mayor parte de mi experiencia usando iteradores en realidad no proviene de C ++ sino de lenguajes de programación de nivel superior como Python.
La interfaz de iterador impone menos requisitos a los consumidores de su función, lo que permite a los consumidores hacer más con ella.
Si todo lo que necesita es poder iterar hacia adelante, el desarrollador no se limita a usar contenedores indexables, pueden usar cualquier clase de implementación operator++(T&)
, operator*(T)
y 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());
Su algoritmo funciona para el caso que lo necesite, iterando sobre un vector, pero también puede ser útil para aplicaciones que no necesariamente anticipa:
#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;
}
Intentar implementar un operador de corchetes que haga algo similar a este iterador sería ingenioso, mientras que la implementación del iterador es relativamente simple. El operador de corchetes también tiene implicaciones sobre las capacidades de su clase, que puede indexar a cualquier punto arbitrario, lo que puede ser difícil o ineficiente de implementar.
Los iteradores también se prestan a la decoración . Las personas pueden escribir iteradores que toman un iterador en su constructor y amplían su funcionalidad:
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;
}
Si bien estos juguetes pueden parecer mundanos, no es difícil imaginar usar iteradores y decoradores de iteradores para hacer cosas poderosas con una interfaz simple: decorar un iterador de resultados de base de datos de solo avance con un iterador que construye un objeto modelo a partir de un solo resultado, por ejemplo . Estos patrones permiten la iteración de conjuntos infinitos con uso eficiente de la memoria y, con un filtro como el que escribí anteriormente, una evaluación de resultados potencialmente perezosa.
Parte de la potencia de las plantillas de C ++ es su interfaz de iterador, cuando se aplica a conjuntos de C de longitud fija, se descompone en aritmética de puntero simple y eficiente , lo que la convierte en una abstracción de costo cero.
some_iterator++
a++some_iterator
. El post-incremento crea un iterador temporal innecesario.