¿Por qué son los rangos de iteradores estándar [begin, end) en lugar de [begin, end]?
¿Por qué el Estándar define end()
como uno más allá del final, en lugar del final real?
7 answers
El mejor argumento fácilmente es el hecho por Dijkstra mismo :
Desea Que el tamaño del rango de ser una simple diferencia end - comenzar;
Incluir el límite inferior es más "natural" cuando las secuencias degeneran a vacías, y también porque la alternativa (excluyendo el límite inferior) requeriría la existencia de un valor centinela "uno-antes-del-principio".
Todavía necesita justificar por qué empiezas a contar en cero en lugar de uno, pero eso no era parte de tu pregunta.
La sabiduría detrás de la convención [begin, end] vale la pena una y otra vez cuando se tiene cualquier tipo de algoritmo que se ocupa de múltiples llamadas anidadas o iteradas a construcciones basadas en rango, que se encadenan naturalmente. Por el contrario, el uso de un rango doblemente cerrado incurriría en código apagado por uno y extremadamente desagradable y ruidoso. Por ejemplo, considere una partición [n0, n1)[n1, n2)[n2,n3). Otro ejemplo es el bucle de iteración estándar for (it = begin; it != end; ++it)
, que se ejecuta end - begin
veces. El código correspondiente sería mucho menos legible si ambos extremos fueran inclusivos, e imagine cómo manejaría los rangos vacíos.
Finalmente, también podemos hacer un buen argumento por qué el conteo debe comenzar en cero: Con la convención semiabierta para rangos que acabamos de establecer, si se le da un rango de elementos N (digamos para enumerar los miembros de una matriz), entonces 0 es el "principio" natural para que pueda escribir el rango como [0, N), sin ningún tipo de compensaciones o correcciones incómodas.
En pocas palabras: el hecho de que no veamos el número 1
en todas partes en los algoritmos basados en rangos es una consecuencia directa y una motivación para la convención [begin, end].
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-04 10:41:40
¿Por qué el Estándar define end()
como uno más allá del final, en lugar del final real?
Porque:
- Evita el manejo especial para rangos vacíos. Para rangos vacíos,
begin()
es igual aend()
& - Hace que el criterio final sea simple para bucles que iteran sobre los elementos: Los bucles simplemente
continúe mientras no se alcance
end()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-01 14:54:12
En realidad, muchas cosas relacionadas con los iteradores de repente tienen mucho más sentido si consideramos que los iteradores no apuntan a los elementos de la secuencia, sino entre, con desreferenciación accediendo al siguiente elemento directamente a él. Entonces el iterador" one past end " de repente tiene sentido inmediato:
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^
| |
begin end
Obviamente begin
apunta al principio de la secuencia, y end
apunta al final de la misma secuencia. La desreferenciación begin
accede al elemento A
, y desreferenciar end
no tiene sentido porque no hay ningún elemento derecho a ello. Además, agregar un iterador i
en el medio da
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
^ ^ ^
| | |
begin i end
Y de inmediato ver que la gama de elementos de begin
a i
contiene los elementos de A
y B
, mientras que la variedad de elementos de i
a end
contiene los elementos de C
y D
. Dereferencing i
da el derecho del elemento de él, que es el primer elemento de la segunda secuencia.
Incluso el "off-by-one" para reverse los iteradores de repente se vuelven obvios de esa manera: Invertir esa secuencia da:
+---+---+---+---+
| D | C | B | A |
+---+---+---+---+
^ ^ ^
| | |
rbegin ri rend
(end) (i) (begin)
He escrito los correspondientes iteradores no inversos (base) entre paréntesis a continuación. Ves, el iterador inverso que pertenece a i
(que he nombrado ri
) todavía puntos entre los elementos B
y C
. Sin embargo, debido a la inversión de la secuencia, ahora element B
está a la derecha de ella.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-17 17:03:05
Porque entonces
size() == end() - begin() // For iterators for whom subtraction is valid
Y no tendrás que hacer cosas incómodas como
// Never mind that this is INVALID for input iterators...
bool empty() { return begin() == end() + 1; }
Y no escribirás accidentalmente código erróneo como
bool empty() { return begin() == end() - 1; } // a typo from the first version
// of this post
// (see, it really is confusing)
bool empty() { return end() - begin() == -1; } // Signed/unsigned mismatch
// Plus the fact that subtracting is also invalid for many iterators
También: ¿Qué devolvería find()
si end()
apuntara a un elemento válido?
¿Realmente quieres otro miembro llamado invalid()
que devuelva un iterador no válido?!
Dos iteradores ya es bastante doloroso...
Oh, y ver esto post relacionado .
También:
Si el end
estaba antes del último elemento, ¿cómo insert()
¿en el verdadero final?!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-01 16:10:59
El modismo iterador de rangos semi-cerrados [begin(), end())
se basa originalmente en la aritmética de punteros para matrices simples. En ese modo de operación, tendría funciones que se pasaron una matriz y un tamaño.
void func(int* array, size_t size)
Convertir a rangos medio cerrados [begin, end)
es muy simple cuando tienes esa información:
int* begin;
int* end = array + size;
for (int* it = begin; it < end; ++it) { ... }
Para trabajar con rangos completamente cerrados, es más difícil:
int* begin;
int* end = array + size - 1;
for (int* it = begin; it <= end; ++it) { ... }
Dado que los punteros a matrices son iteradores en C++ (y la sintaxis fue diseñada para permitir esto), es mucho es más fácil llamar a std::find(array, array + size, some_value)
que llamar a std::find(array, array + size - 1, some_value)
.
Además, si trabaja con rangos medio cerrados, puede usar el operador !=
para verificar la condición final, porque (si sus operadores están definidos correctamente) <
implica !=
.
for (int* it = begin; it != end; ++ it) { ... }
Sin embargo, no hay una manera fácil de hacer esto con rangos completamente cerrados. Estás atascado con <=
.
El único tipo de iterador que soporta operaciones <
y >
en C++ son los iteradores de acceso aleatorio. Si tuvieras que escribir una <=
operador para cada clase de iterador en C++, tendría que hacer que todos sus iteradores sean completamente comparables, y tendría menos opciones para crear iteradores menos capaces (como los iteradores bidireccionales en std::list
, o los iteradores de entrada que operan en iostreams
) si C++ utiliza rangos completamente cerrados.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-01 14:05:22
Con el end()
apuntando al final, es fácil iterar una colección con un bucle for:
for (iterator it = collection.begin(); it != collection.end(); it++)
{
DoStuff(*it);
}
Con end()
apuntando al último elemento, un bucle sería más complejo:
iterator it = collection.begin();
while (!collection.empty())
{
DoStuff(*it);
if (it == collection.end())
break;
it++;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-17 20:12:53
- Si un contenedor está vacío,
begin() == end()
. - Los programadores de C++ tienden a usar
!=
en lugar de<
(menos que) en condiciones de bucle, por lo tanto tenerend()
apuntando a una posición fuera del extremo es conveniente.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-27 15:34:48