Hay muchas funciones útiles <algorithm>
, pero todas operan en "secuencias": pares de iteradores. Por ejemplo, si tengo un contenedor y me gusta ejecutarlo std::accumulate
, necesito escribir:
std::vector<int> myContainer = ...;
int sum = std::accumulate(myContainer.begin(), myContainer.end(), 0);
Cuando todo lo que pretendo hacer es:
int sum = std::accumulate(myContainer, 0);
Lo cual es un poco más legible y más claro, a mis ojos.
Ahora puedo ver que puede haber casos en los que solo desee operar en partes de un contenedor, por lo que definitivamente es útil tener la opción de pasar rangos. Pero al menos en mi experiencia, ese es un caso especial raro. Por lo general, querré operar en contenedores enteros.
Es fácil escribir una función de contenedor que tiene un recipiente y las llamadas begin()
y end()
en él, pero este tipo de funciones de confort no están incluidos en la librería estándar.
Me gustaría saber el razonamiento detrás de esta elección de diseño STL.
fuente
boost::accumulate
Respuestas:
Puede ser un caso especial raro en su experiencia , pero en realidad todo el contenedor es el caso especial, y el rango arbitrario es el caso general.
Ya ha notado que puede implementar todo el caso del contenedor utilizando la interfaz actual, pero no puede hacer lo contrario.
Entonces, el escritor de la biblioteca tenía la opción de implementar dos interfaces por adelantado, o solo implementar una que todavía cubra todos los casos.
Es cierto, especialmente porque las funciones gratuitas
std::begin
ystd::end
ahora están incluidas.Entonces, digamos que la biblioteca proporciona la sobrecarga de conveniencia:
ahora también debe proporcionar la sobrecarga equivalente tomando un functor de comparación, y necesitamos proporcionar los equivalentes para cualquier otro algoritmo.
Pero al menos cubrimos todos los casos en los que queremos operar en un contenedor lleno, ¿verdad? Bueno, no del todo. Considerar
Si queremos manejar el funcionamiento al revés en contenedores, necesitamos otro método (o un par de métodos) por algoritmo existente.
Entonces, el enfoque basado en el rango es más general en el sentido simple de que:
Hay otra razón válida, por supuesto, que es que era ya un montón de trabajo para obtener el STL normalizado, e inflando con envolturas de conveniencia antes de que había sido ampliamente utilizado no sería un gran uso del tiempo comité limitado. Si está interesado, puede encontrar el informe técnico de Stepanov & Lee aquí.
Como se mencionó en los comentarios, Boost.Range proporciona un enfoque más nuevo sin requerir cambios en el estándar.
fuente
f(c.begin(), c.end(), ...)
, y quizás solo a la sobrecarga más comúnmente utilizada (sin embargo, usted determina eso) para evitar duplicar el número de sobrecargas. Además, los adaptadores de iterador son completamente ortogonales (como observa, funcionan bien en Python, cuyos iteradores funcionan de manera muy diferente y no tienen la mayor parte de la potencia de la que habla).std::sort(std::range(start, stop))
.#define MAKE_RANGE(container) (container).begin(), (container).end()
</jk>Resulta que hay un artículo de Herb Sutter sobre este mismo tema. Básicamente, el problema es la sobrecarga de la ambigüedad. Dado lo siguiente:
Y agregando lo siguiente:
Hará difícil distinguir
4
y1
correctamente.Los conceptos, tal como se propusieron pero finalmente no se incluyen en C ++ 0x, lo habrían resuelto, y también es posible eludirlo usando
enable_if
. Para algunos de los algoritmos, no hay ningún problema. Pero decidieron no hacerlo.Ahora, después de leer todos los comentarios y respuestas aquí, creo que los
range
objetos serían la mejor solución. Creo que voy a echar un vistazoBoost.Range
.fuente
typename Iter
parece ser demasiado tipeado para un lenguaje estricto. Yo preferiría por ejemplo,template<typename Container> void sort(typename Container::iterator, typename Container::iterator); // 1
ytemplate<template<class> Container, typename T> void sort( Container<T>&, std::function<bool(const T&)> ); // 4
etc (que quizá resolvería el problema de la ambigüedad)T[]::iterator
disponible. Además, el iterador adecuado no está obligado a ser un tipo anidado de ninguna colección, es suficiente definirlostd::iterator_traits
.Básicamente una decisión heredada. El concepto de iterador se modela en punteros, pero los contenedores no se modelan en matrices. Además, dado que las matrices son difíciles de pasar (en general necesitan un parámetro de plantilla que no sea de tipo para la longitud), a menudo una función solo tiene punteros disponibles.
Pero sí, en retrospectiva, la decisión es incorrecta. Hubiéramos estado mejor con un objeto de rango construible a partir de
begin/end
obegin/length
; ahora tenemos múltiples_n
algoritmos con sufijo en su lugar.fuente
Agregarlos no le otorgaría poder (ya puede hacer todo el contenedor llamando
.begin()
y.end()
usted mismo), y agregaría una cosa más a la biblioteca que debe ser especificada correctamente, agregada a las bibliotecas por los proveedores, probada, mantenida, etcétera etcétera.En resumen, probablemente no esté allí porque no vale la pena mantener un conjunto de plantillas adicionales solo para evitar que los usuarios de contenedores completos escriban un parámetro de llamada de función adicional.
fuente
std::getline
, y aún así, está en la biblioteca. Uno podría ir tan lejos como para decir que las estructuras de control extendidas no me dan poder, ya que podría hacer todo usando soloif
ygoto
. Sí, comparación injusta, lo sé;) Creo que puedo entender el / aplicación / carga de mantenimiento especificación de alguna manera, pero es sólo un pequeño envoltorio que estamos hablando aquí, así que ..Por ahora, http://en.wikipedia.org/wiki/C++11#Range-based_for_loop es una buena alternativa a
std::for_each
. Observe, no hay iteradores explícitos:(Inspirado en https://stackoverflow.com/a/694534/2097284 .)
fuente
<algorithm>
, no todos los algos reales que necesitanbegin
eend
iteradores, ¡pero el beneficio no puede ser exagerado! Cuando probé C ++ 03 por primera vez en 2009, evité los iteradores debido a la repetición de los bucles, y por suerte o no, mis proyectos en ese momento lo permitieron. Al reiniciar en C ++ 11 en 2014, fue una actualización increíble, el lenguaje C ++ siempre debería haber sido, y ahora no puedo vivir sinauto &it: them
:)