Ho una classe C++ che funge da contenitore: ha le funzioni membro size()
e operator[]
. I valori memorizzati "nel" contenitore sono oggetti std::tuple
. Tuttavia, il contenitore non contiene effettivamente le tuple in memoria; invece, li costruisce on-demand in base ai dati sottostanti memorizzati in una forma diversa.Come implementare operator-> per un iteratore che costruisce i suoi valori su richiesta?
std::tuple<int, int, int>
MyContainer::operator[](std::size_t n) const {
// Example: draw corresponding elements from parallel arrays
return { underlying_data_a[n], underlying_data_b[n], underlying_data_c[n] };
}
Quindi, il tipo di ritorno di operator[]
è un oggetto temporaneo, non un riferimento. (Ciò significa che non è un lvalue, quindi il contenitore è di sola lettura, che è OK.)
Ora sto scrivendo una classe iteratore che può essere utilizzata per attraversare le tuple in questo contenitore. Mi piacerebbe modellare RandomAccessIterator, che dipende da InputIterator, ma InputIterator richiede il supporto per l'espressione i->m
(dove i
è un'istanza di iteratore) e, per quanto posso dire, è necessaria una funzione operator->
per restituire un puntatore.
Naturalmente, non posso restituire un puntatore a una tupla temporanea costruita su richiesta. Una possibilità che viene in mente è quello di mettere un'istanza tupla in iteratore come variabile membro, e usarlo per memorizzare una copia di qualsiasi valore dell'iteratore è attualmente posizionato su:
class Iterator {
private:
MyContainer *container;
std::size_t current_index;
// Copy of (*container)[current_index]
std::tuple<int, int, int> current_value;
// ...
};
Tuttavia, l'aggiornamento del valore memorizzato richiederà l'iteratore per verificare se il suo indice corrente è inferiore alla dimensione del contenitore, in modo che un iteratore passato -end-end non causi un comportamento indefinito accedendo oltre la fine degli array sottostanti. Ciò aggiunge (una piccola quantità di) sovraccarico di runtime - non abbastanza per rendere la soluzione poco pratica, ovviamente, ma si sente un po 'poco elegante. L'iteratore non dovrebbe davvero essere necessario memorizzare altro che un puntatore al contenitore che sta iterando e la posizione corrente al suo interno.
Esiste un modo pulito e consolidato per supportare operator->
per i tipi di iteratore che costruiscono i loro valori su richiesta? Come altri sviluppatori farebbero questo genere di cose?
(Si noti che non si ha realmente bisogno per sostenere operator->
a tutti - sto implementando l'iteratore soprattutto in modo che il contenitore può essere attraversato con un C++ 11 "range for" loop, e std::tuple
doesn' t ho membri che normalmente vorremmo accedere via ->
. Ma mi piacerebbe modellare i concetti di iteratore correttamente, tuttavia, mi sento come se stessi tagliando altrimenti altrimenti non dovrei preoccuparmi?)
'operator->' non deve restituire un puntatore. Deve solo restituire qualcosa su cui '*' può essere applicato. –
Hmm, quindi potrei restituire un "puntatore falso" che contiene un valore e restituisce quel valore quando "dereferenziato"? Non sono sicuro se sia più o meno imbarazzante che memorizzare il valore nell'iteratore stesso. :-) Ma almeno (con inlining) ci sarebbe zero overhead di run-time. – Wyzard
In ogni caso, per modellare 'ForwardIterator' è necessario soddisfare questo requisito:" Se aeb sono entrambi dereferenziabili, quindi a == b se e solo se * a e * b sono legati allo stesso oggetto. " Ciò significa in genere che gli iteratori che restituiscono un proxy o che contengono il valore dentro di sé possono essere solo InputIterators –