Una de iteradores en C++

written by uve 29 May 2010

Ayer, mi colega Antares me planteó un problema (y yo no sé decir que no a los problemas). Está desarrollando un contenedor en el cuál necesita tener iteradores e iteradores constantes para recorrer los elementos. Probando los iteradores constantes, utilizó el siguiente código:

Contenedor c;
for(Contenedor::const_iterator it = c.begin(); it != c.end(); ++it)
{
    ...
}

Pero el compilador arrojaba el siguiente error:

contenedor.cpp:474: error: conversion from ‘Contenedor::iterator’ to non-scalar type ‘Contenedor::const_iterator’ requested
contenedor.cpp:474: error: no match for ‘operator!=’ in ‘ite != Contenedor::end()()’
contenedor.h:226: note: candidates are: bool Contenedor::const_iterator::operator!=(const Contenedor::const_iterator&) const

El escenario es el siguiente:

class Contenedor
{
    private:
        ...
    public:
        Contenedor();
        ...
        iterator begin();
        iterator end();
        const_iterator begin() const;
        const_iterator end() const;
        ...
        class iterator
        {
            private:
            ...
            public:
                friend class Contenedor;
                iterator();
                iterator operator++();
                bool operator!=(const iterator & it) const;
                iterator & operator=(const iterator & it) ;

        };
        ...
        class const_iterator
        {
            private:
            ...
            public:
                friend class Contenedor;
                const_iterator();
                const_iterator operator++();
                bool operator!=(const const_iterator & it) const;
                const_iterator & operator=(const const_iterator & it);
                ...
        };
};

¿Dónde está el problema?

Cuando llamas al método begin() de tu clase contenedor, el compilador realiza una llamada al método begin() no-constante de tu clase. Como el objeto c no es constante, no hay forma de saber que se quiere utilizar el método begin() const.

Para solucionar esto podemos hacer un casting a operator, el cuál nos permite que un iterador sea convertido silenciosamente a un const_iterator. Realmente seguimos trabajando con begin() no-constante y con end() no-constante, pero la conversión del iterator es transparente.

La solución es tan sencilla como añadir a la clase iterator el siguiente método:

operator const_iterator() { return const_iterator(); }

También estuvimos barajando otra posibilidad, y fue la de añadir un operator= a iterator que recibiese un const_iterator, con lo cual, también se solucionaría el problema. La desventaja de esto, es que iterator necesitaría acceder a la parte interna de const_iterator.

Tags

La teoría es cuando crees saber algo, pero no funciona.
La práctica es cuando algo funciona, pero no sabes por qué.
Los programadores combinan la teoría y la práctica:
Nada funciona y no saben por qué.