2010-03-13 8 views
7

Tengo una clase que incluye una lista std :: y deseo proporcionar public begin() y end() para const_iterator y private begin() y end() para simplemente iterator.¿Cómo proporcionar stl like container con const iterator público e iterador privado sin const?

Sin embargo, el compilador ve la versión privada y se queja de que es privada en lugar de usar la versión pública de const.

Entiendo que C++ no se sobrecargará en el tipo de devolución (en este caso, const_iterator e iterador) y, por lo tanto, está eligiendo la versión no const ya que mi objeto no es const.

A falta de enviar mi objeto a const antes de llamar a begin() o no sobrecargar el nombre ¿hay alguna manera de lograr esto?

Creo que este es un patrón conocido que la gente ha resuelto antes y me gustaría seguir el ejemplo de cómo esto se resuelve normalmente.

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

El error del compilador es algo como esto:

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

Gracias.

William

+0

Por lo que veo no hay herencia en su código, probablemente debería corregir el 'Estoy derivando una clase de forma privada'. –

+0

Vaya, las ediciones se han desincronizado, gracias por señalarlo, ahora se corrige. – WilliamKF

+1

Si su inicio y fin no const son privados para su contenedor, ¿por qué desea proporcionarles funciones privadas? ¿Por qué no usar los iteradores de listas directamente? – AraK

Respuesta

4

Creo que su única opción es cambiar el nombre de los métodos privados (si los necesita en primer lugar).

Además creo que debería cambiar el nombre de los typedefs:

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(); 
    ... 
}; 

Los contenedores se supone que typedef tanto iterator y const_iterator. Una función genérica que acepte una instancia no const de su contenedor puede esperar usar el typedef iterator, incluso si no va a modificar los elementos. (Por ejemplo, BOOST_FOREACH.)

Estará bien en lo que concierne a la corrección de concordancia, porque si la función genérica realmente intentara modificar los objetos, el tipo de iterador real (que es un const_iterator) no lo permitiría.

Como prueba, la siguiente debe compilar con su contenedor:

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

Tenga en cuenta que m no es constante, sino que sólo están tratando de obtener referencias const a los tipos de contenidos, por lo que este debe haber permitido.

+0

Arg, publicado segundos antes que yo! – Potatoswatter

+0

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

+0

@Emile: AFAIK, usando un guion bajo seguido de un guion bajo el personaje del caso está bien. Personalmente, no usaría este estilo de hecho. – UncleBens

5

Mala idea para derivar a partir std :: lista (que no está diseñado para ser derivado de).

Utilice una variable miembro de 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

Tiene una pregunta actualizada para reflejar esta buena sugerencia, aunque el problema principal aún persiste. Parece que la única solución es renombrar los privados o static_cast para const antes de llamar a begin()/end(). – WilliamKF

4

Debe cambiar el nombre del comienzo privado. El compilador no puede diferenciar por sólo el tipo de retorno

Esto funciona para mí: Anote los nombres _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

Aunque el cambio de nombre hará el truco aquí, usted sigue teniendo problemas si quiere arrojar su objeto a una función genérica esperando 'begin()' y 'end()' ... – xtofl

+0

Parece que esta es la única alternativa viable que no sea haciendo static_cast (contenedor) .begin()/end() para cada llamador. – WilliamKF

+0

@Emilie Cormier No lo sabía. ¡Gracias!. En python es un idioma común para nombrar a los miembros privados con un líder _. @xtofl, si esas funciones son privadas, nadie más que la clase puede usarla, ¿de qué función genérica está hablando? ¿un ejemplo? – fabrizioM

1

Es posible que desee cambiar la firma de su Myfunction método para esto:

void myFunction(const myContainer &container) 

porque se invocaría un método const solo en un objeto const. Lo que está sucediendo actualmente es que está tratando de llamar a un método no const, que en su caso es privado.

+0

Pero en algunos casos eso no sería suficiente, tiene un testcase actualizado para reflejar esto. – WilliamKF

Cuestiones relacionadas