2010-06-28 18 views
10

Attualmente sto cercando di stampare una cronologia di movimenti per i giocatori in un gioco su cui sto lavorando. Alla fine di ogni round, ogni giocatore ha spostato una certa quantità in direzione positiva o negativa e questo viene registrato come int nel vettore di movimento. Alla fine sto cercando di tracciare le direzioni mosse contro il tempo per ciascun giocatore, ma ho difficoltà ad estrarre i dati dal vettore 2D.Iterating over STL vector bidimensionale C++

Quindi la prima cosa che ho provato è stato quello di iterare solo e stampare tutti gli elementi, tuttavia questo non compila:

void output_movement(const std::vector< std::vector<int> > & movement){ 

    std::vector< std::vector<int> >::iterator row; 
    std::vector<int>::iterator col; 
    for (row = movement.begin(); row != movement.end(); ++row) { 
     for (col = row->begin(); col != row->end(); ++col) { 
      std::cout << **col; 
     } 
    } 

} 

Il compilatore fornisce questo messaggio di errore che io non capisco:

hg_competition.cpp:45: error: no match for ‘operator=’ in ‘row = ((const std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >*)money_movement)->std::vector<_Tp, _Alloc>::begin [with _Tp = std::vector<int, std::allocator<int> >, _Alloc = std::allocator<std::vector<int, std::allocator<int> > >]()’ 
/usr/include/c++/4.4/bits/stl_iterator.h:669: note: candidates are: __gnu_cxx::__normal_iterator<std::vector<int, std::allocator<int> >*, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > >& __gnu_cxx::__normal_iterator<std::vector<int, std::allocator<int> >*, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > >::operator=(const __gnu_cxx::__normal_iterator<std::vector<int, std::allocator<int> >*, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > > >&) 

Qualsiasi aiuto è molto apprezzato!

risposta

14

è necessario utilizzare un const_iterator se il vector è un riferimento const. Inoltre, per produrre col devi solo dereferenziarlo una volta.

void output_movement(const std::vector< std::vector<int> > & movement){ 

    std::vector< std::vector<int> >::const_iterator row; 
    std::vector<int>::const_iterator col; 
    for (row = movement.begin(); row != movement.end(); ++row) { 
     for (col = row->begin(); col != row->end(); ++col) { 
      std::cout << *col; 
     } 
    } 
} 

Modifica: usando typedef renderanno il vostro codice più leggibile

typedef std::vector<int> Vector; 
typedef std::vector<Vector> DoubleVector; 

void output_movement(
    const DoubleVector& movement 
) 
{ 
    for (DoubleVector::const_iterator row = movement.begin(); row != movement.end(); ++row) { 
     for (Vector::const_iterator col = row->begin(); col != row->end(); ++col) { 
      std::cout << *col; 
     } 
     std::cout << std::endl; 
    } 
} 
+1

Direi che se hai intenzione di riscriverti Il codice, potrebbe valere la pena di inserire correttamente anche le dichiarazioni 'row' e' col' all'interno del ciclo 'for' per un corretto scope. –

+0

Inoltre sarebbe forse un po 'più simile al C++ se "output_movement" fosse chiamato "operatore <<". – Philipp

+0

@Matthieu Ho aggiornato la mia risposta per includere il tuo suggerimento. –

4

const oggetti restituiscono const_iterators, quindi è sufficiente sostituire iterator per const_iterator ovunque. Ciò impedisce anche modifiche indesiderate dei vettori.

Questa è la combinazione di Sam e suggerimenti di Mathieu:

#include <ostream> 
#include <vector> 

typedef std::vector<int> Vector; 
typedef std::vector<Vector> DoubleVector; 


template<typename Char, typename Traits> 
std::basic_ostream<Char, Traits>& 
operator<<(std::basic_ostream<Char, Traits>& stream, 
      const DoubleVector& movement) { 
    for (DoubleVector::const_iterator row = movement.begin(); row != movement.end(); ++row) { 
     for (Vector::const_iterator col = row->begin(); col != row->end(); ++col) { 
      stream << *col; 
     } 
    } 
return stream; 
} 
12

Il 2D vector è dichiarato const, quindi è necessario utilizzare const_iterator invece di iterator.

Inoltre, non è necessario dereferenziare doppiamente col. È un iteratore, quindi è necessario dereferenziare solo una volta.

void output_movement(const std::vector< std::vector<int> > & movement){ 

    std::vector< std::vector<int> >::const_iterator row; 
    std::vector<int>::const_iterator col; 
    for (row = movement.begin(); row != movement.end(); ++row) { 
     for (col = row->begin(); col != row->end(); ++col) { 
      std::cout << *col; 
     } 
    } 

} 
0

Omg, nulla è migliore di quella confusione di for loop. Ecco alcune alternative. Scegli quello che ti piace.

typedef vector<int> VI; 
typedef vector<VI> VVI; 


namespace std { 
    ostream& operator<<(ostream& o, const VI& v) { 
     copy (v.begin(), v.end(), ostream_iterator<int>(cout)); 
     return o; 
    } 
} 
void output_movement (const VVI& m) { 
    copy (m.begin(), m.end(), ostream_iterator<const VI&>(cout)); 
} 

o,

void output_movement (const VVI & m) { 
    for_each (m.begin(), m.end(), [](const VI& v){ 
       for_each (v.begin(), v.end(), [](int i){ cout << i; }); 
       }); 
} 

o, la mia preferenza personale (boost/foreach.hpp),

void output_movement (const VVI & m) { 
    foreach (const VI& v, m) 
     foreach (int i, v) 
      cout << i; 
} 
3

John, è suggerito di utilizzare lambda, ma se C++ 11 è disponibile, preferirei

for (auto& row : movement) { 
    for (auto& elem : row) { 
     std::cout << elem; 
    } 
} 
+0

+1 per i cicli basati su intervalli puliti - anche se non è necessario il '&', c'è? –

+0

Christian: Sì, c'è! Altrimenti, ogni riga verrà copiata e ripetuta. Il secondo riferimento non è così cruciale. – Petter

+0

Ben: Oh, quello: sì, certamente. Ma allora potresti anche scrivere 'const auto &' per assicurarti di non cambiare accidentalmente i 'row's o' elem'ents, no? –