2010-12-05 18 views
10

Para admitir la noción de STL de rangos semiabiertos, se nos permite señalar uno más allá de una matriz. Supongamos que tenemos un vector de tres elementos. Si std::vector::iterator se implementa como un puntero, como suele ser el caso en las versiones de lanzamiento, a continuación, begin y end punto a estas ubicaciones:¿A dónde apunta el rend?

+---+---+---+.... 
    | | | | . 
    +---+---+---+.... 
    ^  ^
    begin  end 

donde los puntos denotan el pseudo-elemento de una sola past-the-final. Dado que no existe tal cosa como un one-before-the-beginning, ¿dónde exactamente podría indicar rend? Permítanme ilustrar:

+---+---+---+.... 
    | | | | . 
    +---+---+---+.... 
^  ^
rend  rbegin 

Claramente, la ilustración es malo, porque rend es un puntero ilegal. Así que supongo que la implementación de std::vector::reverse_iterator nunca puede ser un puntero, incluso en compilaciones de versiones.

¿Estoy en lo cierto? Entonces, ¿cuál sería la forma más eficiente de implementar reverse_iterator?

Respuesta

3

Hay una diferencia entre a qué apunta lógicamente un reverse_iterator y a qué apunta su iterador. Lógicamente, rbegin produce un iterador que apunta al último elemento de la secuencia y rend produce un iterador que apunta a un elemento antes del inicio. Pero esto generalmente se implementa con un iterador base que apunta a un elemento más allá del elemento al que apunta el iterador inverso. Algo como esto:

template<class Iter> 
class reverse_iter 
{ 
    Iter base; 
public: 
    explicit reverse_iter(Iter it) : base(it) {} 

    reference operator*() const { 
     Iter tmp = base; 
     --tmp; 
     return *tmp; 
    } 

    reverse_iter& operator++() {--base; return *this;} 
}; 

lo tanto, si se inicializa un objeto tal reverse_iter<> con container.end() los puntos de iterador de base a uno más allá del final pero dereferencing el iterador inverso le dará el último elemento. Ningún daño hecho.

5

Como no se permite desreferenciar un iterador que apunta fuera del contenedor, en realidad no importa a qué rend() "apunta". No tiene que ser un valor del puntero legal, puede ser cualquier valor que tenga un significado particular para el tipo contenedor/iterador.

+1

En C, si un puntero no señala a un objeto o un objeto pasado el último elemento, y ese puntero se evalúa (no desreferencia), el comportamiento no está definido. Esta pregunta es sobre C++, pero dudo que haya alguna diferencia allí. Creo que debes aclarar a qué te refieres con: puntos fuera del contenedor. – this

+1

@this: tenga en cuenta que lo que dice es cierto sobre * punteros *, pero no necesariamente sobre * iteradores *. Los iteradores no siempre son indicadores. –

+0

El problema, a mi entender, es que ni siquiera importa si no desreferencia. Ni siquiera podemos ejecutar aritmética de puntero con, o aritmética que evalúa, un puntero 'one-before-the-beginning'. Por lo tanto, no podemos simplemente crear un puntero que apunte antes del comienzo de una matriz y decir que está bien, siempre que no lo desreferenciamos, porque el acto de crearlo o usarlo para medir el tamaño, etc., es UB. ¿O estoy equivocado? –

2

La interfaz std::reverse_iterator incluye una función de miembro .base que recupera un iterador igual al original. Sospecho que lo que suelen hacer es simplemente almacenar en caché el iterador original, y compensarlo por 1 en la sobrecarga del operador *.

+2

Sí, [ eso es lo que hace libstdC++] (http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.4/stl__iterator_8h-source.html#l00150). –

5

El resultado de rbegin puntos a la misma como end (uno más allá del final), y el resultado de rend a la misma como begin (el primer elemento). Cuando se quita la referencia de un iterador inverso, se devuelve una referencia al elemento anterior en el rango.

1

Las otras respuestas responden bien la pregunta.

Pero también me pregunto si sería legal de todos modos, ya que presumiblemente una implementación puede hacer lo que quiera detrás de las escenas, siempre y cuando funcione y cumpla con los estándares. Si elige implementar el iterador como un puntero y elige desreferencia, entonces es responsabilidad de los compiladores saber que funcionará. No podría implementarlo de esta manera, pero me parece que el autor del compilador tiene una licencia especial para hacer esto, ya que sabe cuál será el comportamiento indefinido, y no le exponga ese comportamiento directamente, solo a través de una interfaz de iterador.

+0

Es legal. Los vendedores de compiladores pueden aprovechar su propio UB, y lo hacen todo el tiempo. Un ejemplo común es la macro 'offset_of', que a menudo se implementa en términos de aritmética en un puntero nulo. – MSalters

Cuestiones relacionadas