2010-03-13 14 views
7

Ich habe eine Klasse, die eine std :: list enthält und public begin() und end() für const_iterator und private begin() und end() nur für den einfachen Iterator bereitstellen möchte.Wie stl wie Container mit öffentlichen Const-Iterator und privaten nicht-Const-Iterator bereitstellen?

Allerdings sieht der Compiler die private Version und beschwert sich, dass es privat ist, anstatt die öffentliche const-Version zu verwenden.

Ich verstehe, dass C++ nicht auf Rückgabetyp (in diesem Fall const_iterator und Iterator) überladen wird und daher die nicht-const-Version wählt, da mein Objekt nicht const ist.

Kurz von Gießen mein Objekt zu const vor dem Aufruf von begin() oder nicht überladen der Name beginnen gibt es eine Möglichkeit, dies zu erreichen?

Ich würde denken, dass dies ein bekanntes Muster ist, das die Leute schon einmal gelöst haben und gerne nachziehen würden, wie dies typischerweise gelöst wird.

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. 
} 

Der Fehler vom Compiler ist so etwas wie folgt aus:

../../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 

Dank.

-William

+0

Soweit ich sehe, gibt es keine Vererbung in Ihrem Code, wahrscheinlich sollten Sie korrigieren, ich bin eine Klasse privat abzuleiten '. –

+0

Hoppla, die Änderungen waren nicht mehr synchron, danke, dass Sie darauf hingewiesen haben, jetzt ist es korrigiert. – WilliamKF

+1

Wenn Ihr nicht-constes Begin & End für Ihren Container privat ist, warum möchten Sie private Funktionen für diesen Container bereitstellen? Warum nicht einfach die Listeniteratoren direkt verwenden? – AraK

Antwort

4

Ich denke, Ihre einzige Option ist, die privaten Methoden umzubenennen (wenn Sie sie an erster Stelle benötigen).

Außerdem glaube ich Ihnen die typedefs umbenennen sollte:

sowohl iterator und const_iterator
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(); 
    ... 
}; 

Container sollen typedef. Eine generische Funktion, die eine nicht konstante Instanz des Containers akzeptiert, könnte davon ausgehen, dass sie den Typ iterator typedef verwendet - auch wenn sie die Elemente nicht ändert. (Zum Beispiel BOOST_FOREACH.)

Es wird gut sein, soweit die const Korrektheit geht, denn sollte die generische Funktion tatsächlich versuchen, die Objekte zu ändern, würde der echte Iterator-Typ (ein const_iterator) es nicht zulassen.

Als Test der folgenden sollte mit dem Behälter zusammenstellen:

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

Beachten Sie, dass m nicht const ist, aber wir versuchen, nur const Verweise auf die enthaltenen Typen zu erhalten, so sollte dies sein dürfen.

+0

Arg, posted nur Sekunden vor mir! – Potatoswatter

+0

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

+0

@Emile: AFAIK, mit einem einzelnen Unterstrich gefolgt von einem unteren Groß-/Kleinschreibung ist OK. Persönlich würde ich diesen Stil nicht verwenden. – UncleBens

5

Schlechte Idee ableiten von std :: list (es wird nicht abgeleitet werden von entworfen).

Verwenden Sie eine Membervariable vom Typ 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

Aktualisierte Frage, um diesen guten Vorschlag zu reflektieren, obwohl das Hauptproblem immer noch besteht. Scheint die einzige Lösung ist, entweder die privaten oder static_cast in const vor dem Aufruf von begin()/end() umzubenennen. – WilliamKF

4

Sie müssen den Namen des privaten Begin-Endes ändern. Der Compiler kann unterscheiden nicht nur durch den Rückgabetyp

Dies funktioniert für mich: die _begin _end Namen beachten

#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

Obwohl das Umbenennen den Trick hier machen wird, haben Sie Probleme, wenn Sie Ihr Objekt in eine generische Funktion mit 'begin()' und 'end()' werfen wollen ... – xtofl

+0

Scheint so, als wäre dies die einzig mögliche Alternative außer doing static_cast (container) .begin()/end() für jeden Aufrufer. – WilliamKF

+0

@Emilie Cormier Das wusste ich nicht. Vielen Dank!. In Python ist ein allgemeines Idiom, um die privaten Mitglieder mit einem führenden _ zu benennen. @xtofl, wenn diese Funktionen privat sind, kann niemand außer der Klasse selbst sie benutzen, über welche generische Funktion sprichst du? ein Beispiel? – fabrizioM

1

Sie könnten die Signatur Ihrer Methode Myfunction dazu ändern möchten:

void myFunction(const myContainer &container) 

weil eine const-Methode nur für ein const-Objekt aufgerufen würde. Momentan versuchen Sie, eine nicht-konstante Methode aufzurufen, die in Ihrem Fall privat ist.

+0

Aber in einigen Fällen würde das nicht ausreichen, haben Testfall aktualisiert, um dies zu reflektieren. – WilliamKF

Verwandte Themen