2012-01-12 2 views
8

Ho questo contenitore:Come posso utilizzare BOOST_FOREACH con un contenitore che supporta solo const_iterator?

class /*final*/ Row 
{ 
public: 
    typedef FieldIterator const_iterator; 
    typedef FieldIterator iterator; 
    FieldIterator begin() const; 
    FieldIterator end() const; 
    FieldIterator begin(); 
    FieldIterator end(); 
    ... 
}; 

Dato che, il seguente codice compila bene:

BOOST_FOREACH(Field field, row) 
{ 
} 

Tuttavia, la classe non dovrebbe avere l'iteratore mutevole, così ho cambiato la classe fila, rimuovendo l'accesso mutevole:

class /*final*/ Row 
{ 
public: 
    typedef FieldIterator const_iterator; 
    FieldIterator begin() const; 
    FieldIterator end() const; 
    ... 
}; 

Ma ora lo stesso ciclo foreach non riesce a compilare:

1>o:\c\boost_1_48_0\boost\foreach.hpp(364): error C2039: 'type' : is not a member of 'boost::mpl::eval_if<C,F1,F2>' 
1>   with 
1>   [ 
1>    C=boost::mpl::false_, 
1>    F1=boost::range_const_iterator<sqlserver::Row>, 
1>    F2=boost::range_mutable_iterator<sqlserver::Row> 
1>   ] 
1>   c:\dev\internal\playmssqlce\playmssqlce.cpp(29) : see reference to class template instantiation 'boost::foreach_detail_::foreach_iterator<T,C>' being compiled 
1>   with 
1>   [ 
1>    T=sqlserver::Row, 
1>    C=boost::mpl::false_ 
1>   ] 
... 

Dal messaggio di errore capisco che BOOST_FOREACH tenta di creare un'istanza di un tipo range_mutable_iterator, che non riesce ovviamente. Come faccio a creare un'istanza dell'intervallo costante, invece?

Grazie.

EDIT

Ecco le dichiarazioni complete di classe per e :

class /*final*/ Row 
{ 
    const BYTE *m_buffer; 
    const DBBINDING *m_pColumnBindings; 
    int m_columnBindingCount; 
    FieldIterator m_end; 
public: 
    typedef FieldIterator const_iterator; 
    typedef FieldIterator iterator; 
    Row(const BYTE *buffer, const DBBINDING *pColumnBindings, int columnBindingCount); 
    bool isSameRow(const Row& r) const; 
    int fieldCount() const; 
    Field field(int i) const; 
    Field& field(int i, void *fieldBuffer) const; 
    FieldIterator begin() const; 
    FieldIterator end() const; 
    FieldIterator begin(); 
    FieldIterator end(); 
}; 

class FieldIterator : public iterator_facade<FieldIterator, Field, boost::random_access_traversal_tag> 
{ 
    const Row *m_pRow; 
    int m_index; 
    mutable BYTE m_fieldBuffer[sizeof(Field)]; 
public: 
    FieldIterator(const Row *pRow = NULL, int index = 0); 
private: 
    friend class boost::iterator_core_access; 
    void increment(); 
    void decrement(); 
    void advance(difference_type n); 
    difference_type distance_to(FieldIterator it); 
    reference dereference() const; 
    bool equal(const FieldIterator& rhs) const; 
}; 
+1

'BOOST_FOREACH (const Field & field, row)' funziona? – Ferruccio

+0

Lo stesso risultato esattamente. – mark

risposta

6

A work-around se si vuole veramente evitare che il membro iterator usi una coppia di iteratori.

BOOST_FOREACH(Field field, std::make_pair(row.begin(), row.end())) 
+0

OK, funziona. L'unico problema è che è un po 'troppo prolisso, ma nulla che non possa essere risolto con una macro. – mark

0

sembra essere la stessa classe iteratore per const e metodi iteratore non const. BOOST_FOREACH funziona con qualsiasi contenitore, compresi gli array in stile C, che mi induce a pensare che il problema sia nella classe . Puoi pubblicare il codice per questo?

+0

Fine: guarda il cambiamento nel corpo della domanda. – mark

5

Cosa c'era di sbagliato nel codice originale?

Alcuni dei contenitori di libreria standard, come std::set e std::multiset, hanno iteratori tutti costanti (nessun aggiornamento consentito). Lo standard dice espressamente:

Per contenitori associativi dove il tipo di valore è lo stesso del tipo di chiave, sia iteratore e const_iterator sono iteratori costanti. È non specificato se iteratore e const_iterator sono o meno dello stesso tipo.

Si sarebbe probabilmente ottenere via con

typedef const_iterator iterator; 

nella classe.

+0

Il mio contenitore non è associativo. Quindi il passaggio non si applica al mio codice. – mark

+0

Se si definisce il proprio contenitore, è possibile definirlo in ogni caso. Volevo solo sottolineare che alcuni contenitori standard hanno iteratori che hanno solo accesso in sola lettura agli elementi. Indovina * loro * lavorano con Boost! –

+0

Non voglio neanche definire le versioni mutabili dei metodi 'begin()' e 'end()'. Il tuo consiglio è legittimo, ma eliminando del tutto l'API mutabile, se possibile, è meglio che cortocircuitarlo nell'API const. – mark

0

con boost 1.52 (non ho ancora testato con altre versioni), BOOST_FOREACH(Field field, const_cast<Row const&>(row)) funzionerà anche.