Hice una colección para la que quiero proporcionar un iterador de acceso aleatorio estilo STL. Estaba buscando un ejemplo de implementación de un iterador pero no encontré ninguno. Sé sobre la necesidad de sobrecargas []
y *
operadores constantes . ¿Cuáles son los requisitos para que un iterador sea "STL-style" y cuáles son otras dificultades para evitar (si las hay)?
Contexto adicional: Esto es para una biblioteca y no quiero introducir ninguna dependencia a menos que realmente lo necesite. Escribo mi propia colección para poder proporcionar compatibilidad binaria entre C ++ 03 y C ++ 11 con el mismo compilador (por lo que no hay STL que probablemente se rompa).
c++
iterator
const-iterator
Tamás Szelei
fuente
fuente
Respuestas:
http://www.cplusplus.com/reference/std/iterator/ tiene una útil tabla que detalla las especificaciones del § 24.2.2 del estándar C ++ 11. Básicamente, los iteradores tienen etiquetas que describen las operaciones válidas, y las etiquetas tienen una jerarquía. A continuación es puramente simbólico, estas clases en realidad no existen como tales.
Puede especializarse
std::iterator_traits<youriterator>
o poner los mismos typedefs en el iterador, o heredar destd::iterator
(que tiene estos typedefs). Prefiero la segunda opción, para evitar cambiar las cosas en elstd
espacio de nombres, y para facilitar la lectura, pero la mayoría de las personas heredanstd::iterator
.Tenga en cuenta el iterator_category debería ser uno de
std::input_iterator_tag
,std::output_iterator_tag
,std::forward_iterator_tag
,std::bidirectional_iterator_tag
, ostd::random_access_iterator_tag
, dependiendo de qué requisitos de sus iterador satisface. Dependiendo de su iterador, puede optar por especializarsestd::next
,std::prev
,std::advance
, ystd::distance
, así, pero esto rara vez es necesaria. En casos extremadamente raros , es posible que desee especializarsestd::begin
ystd::end
.Probablemente, su contenedor también debería tener un
const_iterator
iterador (posiblemente mutable) de datos constantes que sea similar al suyo,iterator
excepto que debería ser implícitamente construible desde ay lositerator
usuarios deberían ser incapaces de modificar los datos. Es común que su puntero interno sea un puntero a datos no constantes y que hayaiterator
heredadoconst_iterator
para minimizar la duplicación de código.Mi publicación en Writing your own STL Container tiene un prototipo más completo de contenedor / iterador.
fuente
std::iterator_traits
o definir los typedefs usted mismo, también puede derivar destd::iterator
, que los define por usted, dependiendo de los parámetros de su plantilla.const_iterator
. ¿Qué más faltaba en mi publicación? Parece implicar que hay más para agregar a la clase, pero la pregunta es específicamente sobre la implementación de iteradores.std::iterator
se propuso ser obsoleto en C ++ 17 ; no lo era, pero no confiaría en que estuviera por mucho más tiempo.std::iterator
fue desaprobado después de todo.operator bool
es increíblemente peligroso. Alguien intentará usar eso para detectar el final de un rangowhile(it++)
, pero todo lo que realmente verifica es si el iterador se construyó con un parámetro.La documentación de iterator_facade de Boost.Iterator proporciona lo que parece un buen tutorial sobre la implementación de iteradores para una lista vinculada. ¿Podría usar eso como punto de partida para construir un iterador de acceso aleatorio sobre su contenedor?
Por lo menos, puede echar un vistazo a las funciones de miembro y las definiciones de tipo proporcionadas por
iterator_facade
y usarlo como punto de partida para construir el suyo.fuente
Thomas Becker escribió un artículo útil sobre el tema aquí .
También hubo este enfoque (quizás más simple) que apareció anteriormente en SO: ¿Cómo implementar correctamente iteradores personalizados y const_iterators?
fuente
Aquí hay una muestra de iterador de puntero sin formato.
¡No deberías usar la clase iteradora para trabajar con punteros sin formato!
Solución de bucle basado en rango de puntero sin formato. Por favor, corríjame, si hay una mejor manera de hacer un bucle basado en rango desde un puntero sin formato.
Y prueba simple
fuente
En primer lugar, puede buscar aquí una lista de las diversas operaciones que los tipos de iteradores individuales deben admitir.
A continuación, cuando haya creado su clase de iterador, debe especializarse
std::iterator_traits
y proporcionar algunostypedef
s necesarios (likeiterator_category
ovalue_type
) o, alternativamente, derivarlosstd::iterator
, lo que define lostypedef
s necesarios para usted y, por lo tanto, puede usarse con el valor predeterminadostd::iterator_traits
.Descargo de responsabilidad: Sé que a algunas personas no les gusta
cplusplus.com
mucho, pero proporcionan información realmente útil al respecto.fuente
Estuve / estoy en el mismo barco que usted por diferentes razones (en parte educativas, en parte limitantes). Tuve que volver a escribir todos los contenedores de la biblioteca estándar y los contenedores tenían que cumplir con el estándar. Eso significa que, si cambio mi contenedor con la versión stl , el código funcionaría igual. Lo que también significaba que tenía que volver a escribir los iteradores.
De todos modos, miré a EASTL . Además de aprender mucho sobre contenedores que nunca aprendí todo este tiempo usando los contenedores stl o en mis cursos de pregrado. La razón principal es que EASTL es más legible que la contraparte stl (descubrí que esto se debe simplemente a la falta de todas las macros y al estilo de codificación directo). Hay algunas cosas repugnantes (como #ifdefs para excepciones) pero nada que lo abrume.
Como otros mencionaron, mire la referencia de cplusplus.com sobre iteradores y contenedores.
fuente
Estaba tratando de resolver el problema de poder iterar sobre varios arreglos de texto diferentes, todos los cuales están almacenados en una base de datos residente de memoria que es grande
struct
.Lo siguiente se resolvió utilizando Visual Studio 2017 Community Edition en una aplicación de prueba MFC. Incluyo esto como un ejemplo, ya que esta publicación fue una de varias que encontré y que me brindó ayuda, pero aún no era suficiente para mis necesidades.
El
struct
contenido de los datos residentes de la memoria se parecía a lo siguiente. He eliminado la mayoría de los elementos en aras de la brevedad y tampoco he incluido el preprocesador que se utiliza (el SDK en uso es para C y C ++ y es antiguo).Lo que me interesaba era tener iteradores para las diversas
WCHAR
matrices bidimensionales que contenían cadenas de texto para mnemotécnicos.El enfoque actual es usar una plantilla para definir una clase de proxy para cada una de las matrices y luego tener una sola clase de iterador que se pueda usar para iterar sobre una matriz particular mediante el uso de un objeto proxy que represente la matriz.
Una copia de los datos residentes en la memoria se almacena en un objeto que maneja la lectura y escritura de los datos residentes en la memoria desde / hacia el disco. Esta clase,
CFilePara
contiene la clase de proxy con plantilla (MnemonicIteratorDimSize
y la clase sub partir de la cual es que se deriva,MnemonicIteratorDimSizeBase
) y la clase de iterador,MnemonicIterator
.El objeto proxy creado se adjunta a un objeto iterador que accede a la información necesaria a través de una interfaz descrita por una clase base de la que se derivan todas las clases proxy. El resultado es tener un solo tipo de clase de iterador que se puede usar con varias clases de proxy diferentes porque las diferentes clases de proxy exponen la misma interfaz, la interfaz de la clase base de proxy.
Lo primero era crear un conjunto de identificadores que se proporcionarían a una fábrica de clases para generar el objeto proxy específico para ese tipo de mnemotécnico. Estos identificadores se utilizan como parte de la interfaz de usuario para identificar los datos de aprovisionamiento particulares que el usuario está interesado en ver y posiblemente modificar.
La clase de proxy
La clase proxy con plantilla y su clase base son las siguientes. Necesitaba acomodar varios tipos diferentes de
wchar_t
matrices de cadenas de texto. Las matrices bidimensionales tenían diferentes números de mnemónicos, dependiendo del tipo (propósito) del mnemónico y los diferentes tipos de mnemónicos tenían diferentes longitudes máximas, variando entre cinco caracteres de texto y veinte caracteres de texto. Las plantillas para la clase proxy derivada encajaban naturalmente con la plantilla que requería la cantidad máxima de caracteres en cada mnemónica. Después de crear el objeto proxy, usamos elSetRange()
método para especificar la matriz mnemónica real y su rango.La clase de iterador
La clase iteradora en sí es la siguiente. Esta clase proporciona la funcionalidad básica de iterador directo, que es todo lo que se necesita en este momento. Sin embargo, espero que esto cambie o se extienda cuando necesite algo adicional.
La fábrica de objetos proxy determina qué objeto crear en función del identificador mnemónico. El objeto proxy se crea y el puntero devuelto es el tipo de clase base estándar para tener una interfaz uniforme independientemente de a cuál de las diferentes secciones mnemotécnicas se accede. El
SetRange()
método se utiliza para especificar al objeto proxy los elementos de matriz específicos que representa el proxy y el rango de los elementos de la matriz.Uso de la clase de proxy y el iterador
La clase proxy y su iterador se usan como se muestra en el siguiente ciclo para completar un
CListCtrl
objeto con una lista de mnemotécnicos. Estoy usandostd::unique_ptr
para que cuando la clase proxy ya no la necesite ystd::unique_ptr
quede fuera de alcance, la memoria se limpie.Lo que hace este código fuente es crear un objeto proxy para la matriz dentro del
struct
cual corresponde al identificador mnemotécnico especificado. Luego crea un iterador para ese objeto, usa un rangofor
para completar elCListCtrl
control y luego limpia. Estas son todaswchar_t
cadenas de texto sin procesar que pueden ser exactamente el número de elementos de la matriz, por lo que copiamos la cadena en un búfer temporal para asegurarnos de que el texto esté terminado en cero.fuente
Y ahora un iterador de claves para el bucle basado en rango.
Uso:
Eso es lo que estaba buscando. Pero parece que nadie lo tenía.
Obtiene mi alineación de código OCD como un bono.
Como ejercicio, escribe el tuyo para
values(my_map)
fuente