2010-03-13 5 views
7

Ho una classe che include un elenco std :: e desidero fornire public begin() e end() per const_iterator e private begin() e end() per un semplice iteratore.Come fornire un contenitore di tipo STL con iteratore const pubblico e iteratore privato non const?

Tuttavia, il compilatore sta visualizzando la versione privata e si lamenta che è privato anziché utilizzare la versione pubblica const.

Capisco che C++ non sovraccaricherà sul tipo restituito (in questo caso const_iterator e iterator) e quindi sta scegliendo la versione non-const poiché il mio oggetto non è const.

In mancanza di trasmettere il mio oggetto a const prima di chiamare begin() o non sovraccaricare il nome begin esiste un modo per ottenere ciò?

Penso che questo sia un modello noto che le persone hanno risolto prima e vorrebbe seguire l'esempio di come questo è in genere risolto.

class myObject { 
public: 
    void doSomethingConst() const; 
}; 

class myContainer { 
public: 
    typedef std::list<myObject>::const_iterator const_iterator; 
private: 
    typedef std::list<myObject>::iterator iterator; 

public: 
    const_iterator begin() const { return _data.begin(); } 
    const_iterator end() const { return _data.end(); } 
    void reorder(); 
private: 
    iterator begin() { return _data.begin(); } 
    iterator end() { return _data.end(); } 
private: 
    std::list<myObject> _data; 
}; 

void myFunction(myContainer &container) { 
    myContainer::const_iterator itr = container.begin(); 
    myContainer::const_iterator endItr = container.end(); 
    for (; itr != endItr; ++itr) { 
    const myObject &item = *itr; 
    item.doSomethingConst(); 
    } 
    container.reorder(); // Do something non-const on container itself. 
} 

L'errore del compilatore è qualcosa di simile:

../../src/example.h:447: error: `std::_List_iterator<myObject> myContainer::begin()' is private 
caller.cpp:2393: error: within this context 
../../src/example.h:450: error: `std::_List_iterator<myObject> myContainer::end()' is private 
caller.cpp:2394: error: within this context 

Grazie.

-William

+0

Per quanto vedo, non vi è alcuna ereditarietà nel codice, probabilmente è necessario correggere "Sto generando una classe privatamente". –

+0

Oops, le modifiche non erano sincronizzate, grazie per averlo indicato, ora è stato corretto. – WilliamKF

+1

Se il tuo inizio e fine non const sono privati ​​del tuo contenitore, perché vuoi fornire funzioni private per loro? Perché non usare semplicemente gli iteratori di lista direttamente. – AraK

risposta

4

Penso che la tua unica opzione sia quella di rinominare i metodi privati ​​(se ne hai bisogno in primo luogo).

Inoltre Credo che si dovrebbe rinominare i typedef:

class MyContainer 
{ 
public: 
    typedef std::list<Object>::const_iterator iterator; 
    typedef iterator const_iterator; 

    const_iterator begin() const; 
    const_iterator end() const; 

private: 
    typedef std::list<Object>::iterator _iterator; 
    _iterator _begin(); 
    _iterator _end(); 
    ... 
}; 

I contenitori dovrebbero typedef sia iterator e const_iterator. Una funzione generica che accetta un'istanza non const del tuo contenitore potrebbe aspettarsi di utilizzare il typedef iterator, anche se non modificherà gli elementi. (Per esempio BOOST_FOREACH.)

Va bene per quanto riguarda la correttezza const, perché se la funzione generica tenta effettivamente di modificare gli oggetti, il tipo di iteratore reale (essendo uno const_iterator) non lo consente.

Come test, il seguente dovrebbe compilare con il vostro contenitore:

int main() 
{ 
    myContainer m; 
    BOOST_FOREACH(const myObject& o, m) 
    {} 
} 

noti che m non è const, ma ci sta solo cercando di ottenere riferimenti const ai tipi contenute, quindi questo dovrebbe essere permesso.

+0

Arg, pubblicato pochi secondi prima di me! – Potatoswatter

+0

Vedere http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-identifier –

+0

@Emile: AFAIK, utilizzando un trattino singolo seguito da un valore inferiore il carattere del caso è OK. Personalmente non userei questo stile davvero. – UncleBens

5

cattiva idea deriva da std :: list (che non è progettato per essere derivato da).

Utilizzare una variabile membro di tipo std :: list.

class myContainer 
{ 
    std::list<myObject> m_data; 
    public: 

    typedef std::list<myObject>::const_iterator myContainer::const_iterator; 
    private: 
    typedef std::list<myObject>::iterator myContainer::iterator; 

    public: 

    myContainer::const_iterator begin() const 
    { 
     return m_data.begin(); 
    } 

    myContainer::const_iterator end() const 
    { 
     return m_data.end(); 
    } 

    private: 
    myContainer::iterator begin() 
    { 
     return m_data.begin(); 
    } 

    myContainer::iterator end() 
    { 
     return m_data.end(); 
    } 
}; 
+0

Ho una domanda aggiornata per riflettere questo buon suggerimento, anche se il problema principale rimane ancora. Sembra che la soluzione sia di rinominare i privati ​​o static_cast in const prima di chiamare begin()/end(). – WilliamKF

4

È necessario modificare il nome della fine di inizio privato. Il compilatore non può differenziare da solo il tipo di ritorno

Questo funziona per me: Prendere nota dei nomi _begin _end

#include <list> 


class myObject {}; 

class myContainer : private std::list<myObject> { 
public: 
    typedef std::list<myObject>::const_iterator const_iterator; 
private: 
    typedef std::list<myObject>::iterator iterator; 

public: 
    myContainer::const_iterator begin() const { 
    return std::list<myObject>::begin(); 
    } 
    myContainer::const_iterator end() const { 
    return std::list<myObject>::end(); 
    } 
private: 
    myContainer::iterator _begin() { 
    return std::list<myObject>::begin(); 
    } 
    myContainer::iterator _end() { 
    return std::list<myObject>::end(); 
    } 
}; 

void myFunction(myContainer &container) { 
    myContainer::const_iterator aItr = container.begin(); 
    myContainer::const_iterator aEndItr = container.end(); 
    for (; aItr != aEndItr; ++aItr) { 
    const myObject &item = *aItr; 
    // Do something const on container's contents. 
    } 
} 

int main(){ 
    myContainer m; 
    myFunction(m); 
} 
+1

Anche se il rinominare farà il trucco qui, continui ad avere problemi se vuoi lanciare il tuo oggetto in una funzione generica aspettando 'begin()' e 'end()' ... – xtofl

+0

Sembra che questa sia l'unica alternativa valida che facendo static_cast (container) .begin()/end() per ogni chiamante. – WilliamKF

+0

@ Emilie Cormier Non lo sapevo. Grazie!. In python è un idioma comune chiamare i membri privati ​​con un protagonista _. @xtofl, se quelle funzioni sono private, nessuno ma la classe stessa può usarlo, di quale funzione generica stai parlando? un esempio? – fabrizioM

1

Si potrebbe desiderare di cambiare la firma del metodo Myfunction a questo:

void myFunction(const myContainer &container) 

perché un metodo const può essere chiamato solo su un oggetto const. Quello che sta accadendo attualmente è che stai provando a chiamare un metodo non-const che nel tuo caso è privato.

+0

Ma in alcuni casi ciò non sarebbe sufficiente, abbiamo aggiornato il testcase per riflettere questo. – WilliamKF