2013-05-24 9 views
12

Un solita STL costrutto è:Come funziona std :: copia lavoro con iteratori flusso

vector<string> col; 
copy(istream_iterator<string>(cin), istream_iterator<string>(), 
    back_inserter(col)); 

dove usiamo un istream_iterator di copiare da input std (cin) per un vettore.

Qualcuno può spiegare come funziona questo codice?

il mio problema è che io non capisco proprio questa parte:

istream_iterator<string>(cin), istream_iterator<string>() 
+1

è meglio leggere il libro "The C++ STL" –

risposta

21

Innanzitutto, notare che in questo caso non è necessario utilizzare lo standard std::copy. Si può solo inizializzare il vettore direttamente dalle iteratori:

vector<string> col((istream_iterator<string>(cin)), 
        istream_iterator<string>()); 

Questo probabilmente non rende il codice molto più semplice da capire però.

Per quanto riguarda il funzionamento del codice, è probabilmente un po 'più veloce di quanto si pensi.Un istream_iterator sembra vagamente simile a questo:

template <class T> 
class istream_iterator { 
    std::istream *is; 
    T data; 
public: 
    istream_iterator(std::istream &is) : is(&is) { ++(*this); } 
    istream_iterator() : is(nullptr) {} 

    T operator++() { (*is) >> data; return *this; } 
    T operator++(int) { (*is) >> data; return *this; } 

    T const &operator*() { return data; } 

    bool operator !=(istream_iterator &end) { return (*is).good(); } 
    bool operator ==(istream_iterator &end) { return !(*is).good(); } 
}; 

Ovviamente c'è di più più mi sto saltando, ma che è più di quello che ci sta a cuore qui. Quindi, quello che succede è che quando crei l'iteratore, legge (o tenta di) un elemento dallo stream nella variabile che ho chiamato data. Quando si dereferenzia l'iteratore, restituisce data. Quando si incrementa l'iteratore, legge (o tenta di) l'elemento successivo dal file. Nonostante siano scritti come se confrontassero un iteratore con un altro, operator== e operator!= in realtà basta controllare la fine del file .

che è poi utilizzato per std::copy, che (ancora una volta semplificato) sembra vagamente simile a questo:

template <class InIt, class OutIt> 
void std::copy(InIt b, InIt e, OutIt d) { 
    while (b != e) { 
     *d = *b; 
     ++b; 
     ++d; 
    } 
} 

Quindi, questa legge e voce dal iteratore di input, scrive che voce per l'iteratore di uscita, e si ripete fino a quando il l'iteratore per la posizione corrente è paragonabile all'iteratore per la fine dell'input (che avverrà quando si raggiunge la fine del file). Nota che a differenza di altri iteratori, l'unica posizione di "fine" che puoi utilizzare con un iteratore di istream è la fine del file.


  1. noti che tecnicamente, questo non è conforme comportamento. Ho semplificato il confronto per mantenere le cose semplici. Due iteratori costruiti in modo predefinito devono essere paragonati e, se si costruiscono due iteratori dallo stesso flusso, è necessario confrontarli almeno prima di aver letto qualcosa dallo stream. Ciò tuttavia fa una piccola differenza pratica: l'unico paragone che si è visto nell'uso reale è determinare se si è giunti alla fine del file.
+0

Gli operatori di streaming sono copiabili? –

+0

@quant_dev: intendi iteratori di streaming? Se è così, sì, ma non con molto effetto. In particolare, è possibile copiare l'iteratore e dereferenziare le copie, ma non appena si incrementa uno degli iteratori, il dereferenziazione di qualsiasi copia eccetto quella appena incrementata dà UB. –

5

Parte della risposta qui sotto è citato dalla libreria standard C++: Un tutorial e riferimento da Nicolai M. Josuttis con un po 'di ritocchi.

L'espressione

istream_iterator<string>(cin) 

crea un iteratore stringa che legge dal flusso di input standard cin. L'argomento modello string specifica che l'iteratore di flusso legge elementi di questo tipo. Questi elementi vengono letti con il solito operatore di input >>. Così, ogni volta che l'algoritmo vuole elaborare l'elemento successivo, l'iteratore istream trasforma quel desiderio in una chiamata di

cin >> string 

L'operatore di ingresso per le stringhe di solito legge una parola separate da spazi bianchi.

L'espressione

istream_iterator<string>() 

chiama il costruttore di default degli iteratori IStream che crea un cosiddetto iteratore end-of-stream. Rappresenta un flusso dal quale non puoi più leggere. L'iteratore di fine stringa viene utilizzato come end of the range, quindi l'algoritmo copy legge tutte le stringhe da cin finché non è più possibile leggere più.

L'ultimo:

back_inserter(col)) 

secondo la documentazione back_inserter:

Uno std :: back_insert_iterator che può essere usato per aggiungere elementi alla fine del contenitore c

Aggiungerà tutte le stringhe leggere in col.

È possibile trovare informazioni su std::istream_iterator e std::back_inserter per i dettagli.