Tengo una clase de contenedor personalizada para la que me gustaría escribir iterator
yconst_iterator
clases .
Nunca hice esto antes y no pude encontrar un procedimiento apropiado. ¿Cuáles son las pautas con respecto a la creación de iteradores y qué debo tener en cuenta?
También me gustaría evitar la duplicación de código (siento que const_iterator
yiterator
comparto muchas cosas; ¿debería una subclase a la otra?).
Nota al pie: estoy bastante seguro de que Boost tiene algo para aliviar esto, pero no puedo usarlo aquí, por muchas razones estúpidas.
c++
iterator
const-iterator
ereOn
fuente
fuente
Respuestas:
std::iterator
conrandom_access_iterator_tag
. Estas clases base definen todas las definiciones de tipo requeridas por STL y hacen otro trabajo.Para evitar la duplicación de código, la clase de iterador debe ser una clase de plantilla y debe estar parametrizada por "tipo de valor", "tipo de puntero", "tipo de referencia" o todos ellos (depende de la implementación). Por ejemplo:
Aviso
iterator_type
yconst_iterator_type
definiciones de tipo: son tipos para sus iteradores no constantes y constantes.Ver también: referencia de biblioteca estándar
EDITAR:
std::iterator
está en desuso desde C ++ 17. Vea una discusión relacionada aquí .fuente
random_access_iterator
no está en el estándar y la respuesta no maneja la conversión de mutable a constante. Probablemente quieras heredar de, por ejemplostd::iterator<random_access_iterator_tag, value_type, ... optional arguments ...>
.RefType operator*() { ... }
, estoy un paso más cerca, pero no ayuda, porque todavía lo necesitoRefType operator*() const { ... }
.std::iterator
se propone desaprobar en C ++ 17 .std::iterator
ha quedado en desusoVoy a mostrarle cómo puede definir fácilmente iteradores para sus contenedores personalizados, pero en caso de que haya creado una biblioteca de c ++ 11 que le permita crear fácilmente iteradores personalizados con comportamiento personalizado para cualquier tipo de contenedor, contiguo o no contiguo
Puedes encontrarlo en Github
Estos son los pasos simples para crear y usar iteradores personalizados:
typedef blRawIterator< Type > iterator;
typedef blRawIterator< const Type > const_iterator;
iterator begin(){return iterator(&m_data[0]);};
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
Finalmente, al definir nuestras clases de iterador personalizadas:
NOTA: Al definir iteradores personalizados, derivamos de las categorías de iteradores estándar para que los algoritmos STL conozcan el tipo de iterador que hemos creado.
En este ejemplo, defino un iterador de acceso aleatorio y un iterador de acceso aleatorio inverso:
Ahora en algún lugar de su clase de contenedor personalizado:
fuente
m_data[m_size]
es UB. Simplemente puede solucionarlo reemplazándolo porm_data+m_size
. Para iteradores inversa, tantom_data[-1]
ym_data-1
son incorrectos (UB). Para arreglar reverse_iterators necesitará usar los "punteros al siguiente truco del elemento".A menudo olvidan que
iterator
deben convertirseconst_iterator
pero no al revés. Aquí hay una manera de hacer eso:En el aviso anterior, cómo se
IntrusiveSlistIterator<T>
convierte aIntrusiveSlistIterator<T const>
. SiT
ya es así,const
esta conversión nunca se usa.fuente
const
no aconst
.IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const
?enable_if
podría arreglarlo, pero ...Boost tiene algo para ayudar: la biblioteca Boost.Iterator.
Más precisamente esta página: boost :: iterator_adaptor .
Lo que es muy interesante es el Ejemplo de tutorial que muestra una implementación completa, desde cero, para un tipo personalizado.
El punto principal, como ya se ha citado, es utilizar una implementación de plantilla única y
typedef
esta.fuente
// a private type avoids misuse
enabler
la persona que llama nunca tiene la intención de ser proveedor, por lo que supongo que lo hacen privado para evitar que las personas intenten pasarlo accidentalmente. No creo, de ninguna manera, que pueda crear ningún problema para pasarlo, ya que la protección radica enenable_if
.No sé si Boost tiene algo que pueda ayudar.
Mi patrón preferido es simple: tome un argumento de plantilla que sea igual a
value_type
, calificado o no const. Si es necesario, también un tipo de nodo. Entonces, bueno, todo se acomoda.Solo recuerde parametrizar (template-ize) todo lo que debe ser, incluido el constructor de copia y
operator==
. En su mayor parte, la semántica deconst
creará un comportamiento correcto.fuente
cur
ilustré , pero podría haber un problema al acceder desde el iterador de constidad opuesta. La solución que viene a mi mente esfriend my_container::const_iterator; friend my_container::iterator;
, pero no creo que sea así como lo hice antes ... de todos modos este esquema general funciona.friend class
en ambos casos.Hay muchas buenas respuestas, pero creé un encabezado de plantilla que uso que es bastante conciso y fácil de usar.
Para agregar un iterador a su clase, solo es necesario escribir una clase pequeña para representar el estado del iterador con 7 funciones pequeñas, de las cuales 2 son opcionales:
Entonces puede usarlo como esperaría de un iterador STL:
Espero que ayude.
fuente