Ho sentito che nessuna delle risposte qui spiega perché mi piacciono gli iteratori come concetto generale rispetto all'indicizzazione in contenitori. Nota che la maggior parte della mia esperienza con gli iteratori non proviene in realtà dal C ++ ma da linguaggi di programmazione di livello superiore come Python.
L'interfaccia iteratore impone meno requisiti ai consumatori della tua funzione, il che consente ai consumatori di fare di più con esso.
Se tutto ciò che serve è essere in grado di inoltrare-iterazioni, lo sviluppatore non si limita all'utilizzo di contenitori intercambiabili - possono utilizzare qualsiasi classe che implementa operator++(T&)
, operator*(T)
e 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());
Il tuo algoritmo funziona nel caso in cui ne hai bisogno - iterando su un vettore - ma può anche essere utile per applicazioni che non prevedi necessariamente:
#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;
}
Tentare di implementare un operatore tra parentesi quadre che fa qualcosa di simile a questo iteratore sarebbe inventato, mentre l'implementazione dell'iteratore è relativamente semplice. L'operatore parentesi quadre ha anche implicazioni sulle capacità della tua classe - che puoi indicizzare su qualsiasi punto arbitrario - che può essere difficile o inefficiente da implementare.
Gli iteratori si prestano anche alla decorazione . Le persone possono scrivere iteratori che prendono un iteratore nel loro costruttore e ne estendono la funzionalità:
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;
}
Mentre questi giocattoli possono sembrare banali, non è difficile immaginare di usare iteratori e decoratori di iteratori per fare cose potenti con una semplice interfaccia: decorare un iteratore solo in avanti dei risultati del database con un iteratore che costruisce un oggetto modello da un singolo risultato, ad esempio . Questi schemi consentono l'iterazione efficiente di memoria di insiemi infiniti e, con un filtro come quello che ho scritto sopra, valutazione potenzialmente pigra dei risultati.
Parte della potenza dei modelli C ++ è la tua interfaccia iteratore, quando applicata a maglie simili di array C a lunghezza fissa, decade in aritmetica puntatore semplice ed efficiente , rendendola un'astrazione a costo zero.
some_iterator++
a++some_iterator
. Post-incremento crea un iteratore temporaneo non necessario.