2008-10-30 8 views
8

sto cercando di codice di azione opposto a questo:Come leggere un numero arbitrario di valori usando std :: copy?

std::ostream outs; // properly initialized of course 
std::set<int> my_set; // ditto 

outs << my_set.size(); 
std::copy(my_set.begin(), my_set.end(), std::ostream_iterator<int>(outs)); 

dovrebbe essere qualcosa di simile:

std::istream ins; 

std::set<int>::size_type size; 
ins >> size; 

std::copy(std::istream_iterator<int>(ins), std::istream_iterator<int>(ins) ???, std::inserter(my_set, my_set.end())); 

Ma io sono bloccato con i iteratore 'fine' - interators ingresso può usare std :: advance e nè posso usare due flussi con la stessa fonte ...

C'è un modo elegante per risolvere questo? Certo che posso usare per il ciclo, ma forse c'è qualcosa di più bello :)

risposta

3

È possibile derivare dall'istream_iterator <T>.
Anche se l'utilizzo di Daemin generator method è un'altra opzione, anche se genererei direttamente nell'insieme anziché utilizzare un vettore intermedio.

#include <set> 
#include <iterator> 
#include <algorithm> 
#include <iostream> 


template<typename T> 
struct CountIter: public std::istream_iterator<T> 
{ 
    CountIter(size_t c) 
     :std::istream_iterator<T>() 
     ,count(c) 
    {} 
    CountIter(std::istream& str) 
     :std::istream_iterator<T>(str) 
     ,count(0) 
    {} 

    bool operator!=(CountIter const& rhs) const 
    { 
     return (count != rhs.count) && (dynamic_cast<std::istream_iterator<T> const&>(*this) != rhs); 
    } 
    T operator*() 
    { 
     ++count; 
     return std::istream_iterator<T>::operator*(); 
    } 

    private: 
     size_t count; 
}; 

int main() 
{ 
    std::set<int>  x; 

    //std::copy(std::istream_iterator<int>(std::cin),std::istream_iterator<int>(),std::inserter(x,x.end())); 
    std::copy(
       CountIter<int>(std::cin), 
       CountIter<int>(5), 
       std::inserter(x,x.end()) 
      ); 
} 
+2

mentre questa è una soluzione molto interessante, si dovrebbe notare che semplicemente scrivere un loop e non farlo sarebbe più corto –

+0

Hai usato dynamic_cast su un tipo non porfirico, non dovrebbe compilare –

+0

@Armen Tsirunyan: La tua argomentazione è autodistruttiva. Hai ragione nella tua asserzione che i tipi non polimorfici non verranno compilati se usati con dynamic_cast. Eppure quanto sopra è compilato e funziona correttamente. Così da Ramser di occam ci rimane la conclusione che si tratta di un tipo polimorfico. Grazie e buona notte. :-) –

0

(a cura: avrei dovuto leggere la questione più da vicino ...)

Mentre un po 'sospetto, è possibile ottenere circa il giusto comportamento da avere una voce nel file che "fallirà" il primo ciclo, quindi cancellerà il bit di fail sul flusso e inizierà a leggere di più.

dati, senza una dimensione esplicita, ma in questo modo

 
1 1 2 3 5 8 Fibb 

Fed il codice qui sotto sembra di fare quello che volevo dire, almeno su VS2005 con STLPort.

 
typedef std::istream_iterator < int, char, std::char_traits ,ptrdiff_t> is_iter; 
std::copy(is_iter(cin), is_iter(), inserter(my_set,my_set.end())); 
cin.clear(); 
std::cin >> instr; 
+0

Hehe, hai ragione, è una specie di sospetto e la più importante - non funzionerà sul flusso binario :) –

0

Sì sdg ma quando voglio utilizzare un'altra struttura di dati in quel file/flusso? Probabilmente dovrei scrivere esplicitamente qui, voglio archiviare un'altra roba dopo questo set, questo è il motivo per cui sto memorizzando anche la dimensione.

2

Errr ... copy_n() algoritmo?

+0

"Questa funzione è un'estensione SGI; non è parte dello standard C++. " E questo è molto vero, manca in VS2008 include almeno. Ma bello, volevo quasi incolpare me stesso di come è possibile che l'ho ignorato;) –

+1

C++ 0x è aggiungendo anche questo –

2

Guardando in questo un po 'non penso che leggere direttamente in un set funzionerà, poichè è necessario chiamare insert su di esso per aggiungere effettivamente gli elementi (potrei sbagliarmi, è piuttosto presto la mattina qui) . Anche se guardando la documentazione STL in VS2005 brevemente Credo che qualcosa si utilizza la funzione generate_n dovrebbe funzionare, per esempio:

std::istream ins; 
std::set<int> my_set; 
std::vector<int> my_vec; 

struct read_functor 
{ 
    read_functor(std::istream& stream) : 
     m_stream(stream) 
    { 
    } 

    int operator() 
    { 
     int temp; 
     m_stream >> temp; 
     return temp; 
    } 
private: 
    std::istream& m_stream; 
}; 

std::set<int>::size_type size; 
ins >> size; 
my_vec.reserve(size); 

std::generate_n(my_vec.begin(), size, read_functor(ins)); 
my_set.insert(my_vec.begin(), my_vec.end()); 

Speriamo che è sia risolto il problema, oppure è convinto che il ciclo non è così male nel grande schema di cose.

+1

Perché usare un vettore come intermedio? Basta usare il generatore per inserirlo nel set (usando std :: inserter). –

+0

Era tardi quando ho composto la risposta, e non mi è venuto in mente in quel momento Immagino che usereste un inserter, anche se richiede ancora la creazione di un'intera classe, quindi per me un ciclo sarebbe più semplice e probabilmente anche il migliore. – Daemin

1

ne dite di usare un iteratore alternativo per fare la traversata e quindi utilizzare un oggetto funzione (o lambda) per riempire il serbatoio?

istream ins; 
set<int>::size_type size; 
set<int> new_set; 
ins >> size; 
ostream_iterator<int> ins_iter(ins); 

for_each(counting_iterator<int>(0), counting_iterator<int>(size), 
    [&new_set, &ins_iter](int n) { new_set.insert(*ins_iter++); } 
); 

Naturalmente si presuppone che si abbia un compilatore C++ 0x compatibile.

BTW, 'counting_iterator <>' fa parte di Boost.Iterator.

0

Grazie per le idee ragazzi.Anche quando queste cose sembrano fresco, ho certo intenzione di creare nuova classe/iteratore per questo ;-) Ho capire meglio il motivo per cui SGI ha deciso di includere algoritmo "copy_n" ora :)

3

Usa:

std::copy(std::istream_iterator<int>(ins), 
      std::istream_iterator<int>(), 
      std::inserter(my_set, my_set.end()) 
     ); 

Nota il parametro vuoto:

std::istream_iterator<int>(); 
+0

Dannazione, grazie! Potrei giurare che ho provato questo ma come sembra che non l'abbia fatto. Funziona alla grande, molte grazie! (nota a margine: è sempre bello occuparsi dei propri post :) –

+0

D'altra parte, come si potrebbe definire ad esempio la lettura di tre byte, non l'intero file? ;) Quindi questo era il mio problema nell'accettare la tua risposta allora, ora ricordo. –

1

Oppure si potrebbe fare questo:

my_set.insert(std::istream_iterator<int>(ins), std::istream_iterator<int>());