librerias libreria estandar dev contenedores c++ stl iterator

c++ - libreria - ¿Puedo/debo heredar de iterador STL?



librerias estandar dev c++ (4)

Respuesta corta

Muchos consideran que la clase std::iterator no ofrece muchos alias de tipo regular, e incluso los confunde un poco al no proporcionar explícitamente los nombres y confiar en el orden de los parámetros de la plantilla. Está en desuso en C ++ 17 y es probable que desaparezca en unos pocos años.

Esto significa que ya no deberías usar std::iterator . Puede leer la publicación completa a continuación si está interesado en la historia completa (hay un poco de redundancia ya que se inició antes de la propuesta de desaprobación).

Respuesta legada

Puedes ignorar todo lo que aparece a continuación si no estás interesado en la historia. Los siguientes fragmentos incluso se contradicen varias veces.

A partir de hoy (C ++ 11 / C ++ 14), el estándar parece implicar que ya no es una buena idea heredar de std::iterator para implementar iteradores personalizados. Aquí hay una breve explicación, de N3931 :

Aunque el Estándar ha cometido este error casi una docena de veces, recomiendo no representar directory_iterator y recursive_directory_iterator como derivado de std::iterator , ya que es un requisito vinculante en las implementaciones. En su lugar, deben representarse con las definiciones de tipo adecuadas, y dejar que los implementadores decidan cómo proporcionarlas. (La diferencia es observable para los usuarios con is_base_of , no es que deban hacer esa pregunta).

[2014-02-08 Daniel comenta y proporciona una redacción]

Este problema es básicamente similar al tipo de solución que se usó para eliminar el requisito de derivar de la función unary_function y los amigos tal como se describe en N3198 y estoy firmemente a favor de seguir ese espíritu aquí también. Me gustaría agregar que, básicamente, todos los tipos de iterador "más nuevos" (como el iterador relacionado con expresiones regex ) tampoco se derivan de std::iterator .

El documento cita N3198 que a su vez afirma que sigue la N3145 discutida en N3145 . Las razones para desaprobar las clases que solo existen para proporcionar typedef s se dan como tales:

Nuestra experiencia con los conceptos nos da la confianza de que rara vez es necesario depender de las relaciones de clase derivadas de la clase base específica, si la disponibilidad de tipos y funciones es suficiente. Las nuevas herramientas de lenguaje nos permiten, incluso en ausencia de conceptos respaldados por lenguaje, deducir la existencia de nombres de tipos en los tipos de clase, lo que introduciría un acoplamiento mucho más débil entre ellos. Otra ventaja de reemplazar la herencia por tipos asociados es el hecho de que esto reducirá el número de casos, donde surgen ambigüedades: Esto puede suceder fácilmente, si un tipo heredaría tanto de unary_function como de binary_function (Esto tiene sentido, si un funtor es a la vez un objeto de función unario y binario).

tl; dr : las clases que solo proporcionan typedef s ahora se consideran inútiles. Además, aumentan el acoplamiento cuando no es necesario, son más detallados y pueden tener efectos secundarios no deseados en algunos casos de esquina (consulte la cita anterior).

Actualización: el problema 2438 de N4245 parece contradecir lo que afirmé anteriormente:

Para conveniencia de los LWG, se representa a nueve iteradores STL como derivados de std::iterator para obtener su iterator_category / etc. typedefs. Desafortunadamente (y sin querer), esto también exige la herencia, que es observable (no solo a través de is_base_of , sino también de resolución de sobrecarga). Esto es desafortunado porque confunde a los usuarios, quienes pueden confundirse al pensar que sus propios iteradores deben derivarse de std::iterator , o que las funciones de sobrecarga para tomar std::iterator son de alguna manera significativas. Esto tampoco es intencional porque los iteradores más importantes de la STL, los iteradores de contenedor, no tienen que derivar de std::iterator . (Algunos incluso pueden ser punteros en bruto). Finalmente, esto restringe innecesariamente a los implementadores, que pueden no querer derivar de std::iterator . (Por ejemplo, para simplificar las vistas del depurador).

En resumen, me equivoqué, @aschepler tenía razón: se puede usar, pero ciertamente no es necesario, tampoco está desanimado. Todo el asunto de "eliminemos std::iterator " existe para que el estándar no restrinja a los implementadores de la biblioteca estándar.

Ronda 3: P0174R0 propone P0174R0 std::iterator para una posible eliminación en el futuro. La propuesta ya es bastante buena para explicar por qué debería ser desaprobada, así que aquí vamos:

La larga secuencia de argumentos vacíos es mucho menos clara para el lector que simplemente proporcionar los typedefs esperados en la definición de clase en sí misma, que es el enfoque adoptado por el borrador de trabajo actual, siguiendo el patrón establecido en C ++ 14 donde desaprobamos la derivación en toda la biblioteca de functors desde unary_function y binary_function.

Además de la claridad reducida, la plantilla del iterador también crea una trampa para los incautos, ya que en el uso típico será una clase base dependiente, lo que significa que no se buscará durante la búsqueda de nombres desde la clase o sus funciones miembro. Esto lleva a los usuarios sorprendidos que intentan comprender por qué el siguiente uso simple no funciona:

#include <iterator> template <typename T> struct MyIterator : std::iterator<std::random_access_iterator_tag, T> { value_type data; // Error: value_type is not found by name lookup // ... implementations details elided ... };

El motivo de claridad solo fue suficiente para persuadir al LWG de que actualice la especificación de la biblioteca estándar para que ya no exija que los adaptadores de iterador estándar se deriven de std :: iterator, por lo que no hay más uso de esta plantilla dentro del propio estándar. Por lo tanto, parece un candidato fuerte para la desaprobación.

Esto se está volviendo un poco agotador y no todos parecen estar de acuerdo, por lo que les dejaré sacar sus propias conclusiones. Si el comité finalmente decide que std::iterator debe estar en desuso, entonces dejará bastante claro que ya no debe usarlo. Tenga en cuenta que el documento de seguimiento destaca un gran soporte para la eliminación de std::iterator :

Actualización de Jacksonville, 2016:

Encuesta: iterator obsoleto para C ++ 17 ??
SF F N A SA
6 10 1 0 0

En los resultados de la encuesta anterior, SF , F , N , A y SA son Fuertes , Neutrales , Contra y Fuertes Contra .

Actualización de Oulu, 2016:

Encuesta: ¿ Todavía quieres desaprobar std::iterator?
SF FNA SA
3 6 3 2 0

P0619R1 propone eliminar std::iterator , posiblemente tan pronto como C ++ 20, y también propone mejorar std::iterator_traits para que pueda deducir automáticamente los tipos difference_type , pointer y reference la manera en que std::iterator hace cuando no se proporciona explícitamente.

¿Puedo / debo heredar de iterador STL para implementar mi propia clase de iterador? Si no, ¿por qué no?


Nadie debe hacerlo debido a los problemas potenciales que pueden surgir. Probablemente esté mejor usando Composición en lugar de Herencia con Iteradores STL.

Comportamiento indefinido debido a la ausencia de destructores virtuales:
Los contenedores e iteradores de STL no están diseñados para actuar como clases base, ya que no tienen destructores virtuales.

Para las clases que no utilizan destructores virtuales como clase base, el problema surge cuando se desasigna a través de un puntero a la clase base (eliminar, eliminar [], etc.). Dado que las clases no tienen destructores virtuales, no se pueden limpiar correctamente y dan como resultado un comportamiento indefinido.

Uno podría argumentar que no habría necesidad de eliminar polimorfamente el iterador y, por lo tanto, no hay nada de malo en proceder con los iteradores STL, y puede que haya otros problemas como:

La herencia puede no ser posible en absoluto:
Todos los tipos de iteradores en el contenedor estándar son Implementación definida .
Por ejemplo: std::vector<T>::iterator podría ser solo un T* . En este caso, simplemente no puede heredar de él.

El estándar C ++ no tiene disposiciones que exijan que, por ejemplo, std::vector<T>::iterator no utilice técnicas de inhibición de la herencia para evitar la derivación. Por lo tanto, si está derivando de un iterador STL, está confiando en una característica de su STL que permite la derivación. Eso hace que tal implementación no sea portátil .

Comportamientos de buggy si no se implementan adecuadamente:
Considera que estás derivando de una clase de iteradores vectoriales como:

class yourIterator : std::vector<T>::iterator { ... };

Puede haber una función que opera en los iteradores vectoriales,
Por ejemplo:

void doSomething(std::vector<T>::iterator to, std::vector<T>::iterator from);

Ya que su yourIterator es un yourIterator std::vector<T>::iterator puede llamar doSomething() en su clase contenedora pero enfrentará el problema feo de la segmentación de Object Slicing . El doSomething() debe implementarse de manera apropiada para evitar el problema.

Problemas durante el uso de algoritmos de biblioteca estándar:
Considere que está usando la derivación del vector iterador, y luego usa un algoritmo de biblioteca estándar como std::transform()

Por ejemplo:

yourIterator a; yourIterator b; ... std::transform( a++, b--, ... );

El operator ++ postfix operator ++ devuelve un std::vector<T>::iterator yourIterator y no un yourIterator lo que hace que se yourIterator una plantilla incorrecta.

Por lo tanto, la herencia de los iteradores STL es posible, pero si está listo para desenterrar todos esos problemas potenciales y muchos otros y abordarlos, Personalmente no le daré el tiempo y el esfuerzo para hacerlo.


Si estás hablando de std::iterator template, entonces sí, deberías, pero espero que entiendas que no tiene ninguna funcionalidad, solo un montón de typedefs. El pro de esta decisión es que su iterador se puede enviar a la plantilla iterator_traits .

Si, por otro lado, estás hablando de algún iterador STL específico, como vector<T>::iterator u otro, entonces la respuesta es un NO rotundo. Dejando de lado todo lo demás, no está seguro de que sea realmente una clase (por ejemplo, el mismo vector<T>::iterator puede ser tipificado como T* )


Si te refieres a std::iterator : sí, para eso es.

Si quiere decir algo más: no, porque ninguno de los iteradores de STL tiene destructores virtual . No están destinados a la herencia y una clase heredada de ellos puede que no se limpie correctamente.