Cada contenedor estándar tiene un método begin
y end
para devolver iteradores para ese contenedor. Funciones gratuitas Sin embargo, C ++ 11 aparentemente ha introducido llaman std::begin
y std::end
que exigen las begin
y end
miembros de funciones. Entonces, en lugar de escribir
auto i = v.begin();
auto e = v.end();
tu escribirias
auto i = std::begin(v);
auto e = std::end(v);
En su charla, Writing Modern C ++ , Herb Sutter dice que siempre debe usar las funciones gratuitas ahora cuando desee el iterador de inicio o finalización para un contenedor. Sin embargo, no entra en detalles sobre por qué querrías hacerlo. Mirando el código, te ahorra un solo personaje. Entonces, en lo que respecta a los contenedores estándar, las funciones gratuitas parecen ser completamente inútiles. Herb Sutter indicó que había beneficios para los envases no estándar, pero nuevamente, no entró en detalles.
Entonces, la pregunta es, ¿qué hacen exactamente las versiones de funciones gratuitas std::begin
y lo que std::end
hacen más allá de llamar a sus versiones de funciones miembro correspondientes, y por qué querría usarlas?
std::
todo el tiempo.Respuestas:
¿Cómo se llama
.begin()
y.end()
en un C-array?Las funciones libres permiten una programación más genérica porque se pueden agregar después, en una estructura de datos que no se puede modificar.
fuente
end
matrices declaradas estáticamente (int foo[5]
) utilizando trucos de programación de plantillas. Una vez que se ha convertido en un puntero, por supuesto, no tienes suerte.template<typename T, size_t N> T* end(T (&a)[N]) { return a + N; }
begin
yend
en una matriz C siempre y cuando no lo haya convertido usted mismo en un puntero, @Huw lo explica. En cuanto a por qué querrías: imagina que refactorizaste el código que estaba usando una matriz para usar un vector (o viceversa, por cualquier razón). Si ha estado usandobegin
yend
, y tal vez algún tipo de definición inteligente, el código de implementación no tendrá que cambiar en absoluto (excepto quizás algunos de los typedefs).Considere el caso cuando tiene una biblioteca que contiene clase:
tiene 2 métodos:
iterar sobre ella los valores que necesita heredar de esta clase y definir
begin()
yend()
métodos para los casos enPero si siempre usas
Puedes hacerlo:
donde
SpecialArrayIterator
es algo como:ahora
i
y see
puede usar legalmente para iterar y acceder a valores de SpecialArrayfuente
template<>
líneas. Está declarando una nueva sobrecarga de funciones, no especializando una plantilla.El uso de las funciones
begin
yend
gratis agrega una capa de indirección. Por lo general, eso se hace para permitir más flexibilidad.En este caso se me ocurren algunos usos.
El uso más obvio es para matrices en C (no punteros en C).
Otra es cuando se intenta utilizar un algoritmo estándar en un contenedor no conforme (es decir, al contenedor le falta un
.begin()
método). Suponiendo que no puede simplemente arreglar el contenedor, la siguiente mejor opción es sobrecargar labegin
función. Herb sugiere que siempre use labegin
función para promover la uniformidad y consistencia en su código. En lugar de tener que recordar qué contenedores admiten el métodobegin
y cuáles necesitan funcionarbegin
.Como un aparte, rev la siguiente C ++ debería copiar D's notación pseudo-miembro . Si
a.foo(b,c,d)
no está definido, en su lugar lo intentafoo(a,b,c,d)
. Es solo un poco de azúcar sintáctica para ayudarnos a los humanos pobres que prefieren el orden del sujeto al orden verbal.fuente
Para responder a su pregunta, las funciones gratuitas begin () y end () por defecto no hacen más que llamar a las funciones miembro .begin () y .end () del contenedor. Desde
<iterator>
, se incluye de forma automática cuando se utiliza cualquiera de los contenedores estándar como<vector>
,<list>
, etc., que se obtiene:La segunda parte de su pregunta es por qué prefieren las funciones gratuitas si lo único que hacen es llamar a las funciones miembro de todos modos. Eso realmente depende de qué tipo de objeto
v
está en su código de ejemplo. Si el tipo de v es un tipo de contenedor estándar, por ejemplovector<T> v;
, no importa si usa las funciones gratuitas o miembro, hacen lo mismo. Si su objetov
es más genérico, como en el siguiente código:Luego, el uso de las funciones miembro rompe el código para las matrices T = C, las cadenas C, las enumeraciones, etc. Al usar las funciones que no son miembros, anuncia una interfaz más genérica que las personas pueden ampliar fácilmente. Al usar la interfaz de función libre:
El código ahora funciona con matrices T = C y cadenas C. Ahora escribiendo una pequeña cantidad de código de adaptador:
También podemos hacer que su código sea compatible con enumeraciones iterables. Creo que el punto principal de Herb es que usar las funciones gratuitas es tan fácil como usar las funciones miembro, y le da a su código compatibilidad hacia atrás con tipos de secuencia C y compatibilidad hacia adelante con tipos de secuencia no stl (¡y tipos stl futuros!), con bajo costo para otros desarrolladores.
fuente
enum
u otro tipo fundamental por referencia; serán más baratos de copiar que indirectos.Una ventaja de
std::begin
ystd::end
es que sirven como puntos de extensión para implementar una interfaz estándar para clases externas.Si desea utilizar la
CustomContainer
clase con función de bucle o plantilla basada en rango que espera.begin()
y.end()
métodos, obviamente tendría que implementar esos métodos.Si la clase proporciona esos métodos, eso no es un problema. Cuando no es así, deberías modificarlo *.
Esto no siempre es factible, por ejemplo, cuando se utiliza una biblioteca externa, especialmente comercial y de fuente cerrada.
En tales situaciones,
std::begin
ystd::end
resulta útil, ya que uno puede proporcionar API de iterador sin modificar la clase en sí, sino más bien sobrecargar funciones libres.Ejemplo: suponga que desea implementar una
count_if
función que tome un contenedor en lugar de un par de iteradores. Tal código podría verse así:Ahora, para cualquier clase que desee utilizar con esta costumbre
count_if
, solo tiene que agregar dos funciones libres, en lugar de modificar esas clases.Ahora, C ++ tiene un mecanismo llamado Búsqueda dependiente de argumentos (ADL), que hace que este enfoque sea aún más flexible.
En resumen, ADL significa que cuando un compilador resuelve una función no calificada (es decir, una función sin espacio de nombres, como en
begin
lugar destd::begin
), también considerará las funciones declaradas en los espacios de nombres de sus argumentos. Por ejemplo:En este caso, no importa que los nombres calificados lo estén
some_lib::begin
ysome_lib::end
, dadoCustomContainer
quesome_lib::
también lo está, el compilador usará esas sobrecargascount_if
.Esa es también la razón para tener
using std::begin;
yusing std::end;
entrarcount_if
. Esto nos permite usar no calificadobegin
yend
, por lo tanto, permitir ADL y permitir que el compilador elijastd::begin
ystd::end
cuando no se encuentren otras alternativas.Podemos comer la cookie y tener la cookie, es decir, tener una manera de proporcionar una implementación personalizada de
begin
/end
mientras el compilador puede recurrir a las estándar.Algunas notas:
Por la misma razón, hay otras funciones similares:
std::rbegin
/rend
,std::size
ystd::data
.Como se menciona en otras respuestas, las
std::
versiones tienen sobrecargas para matrices desnudas. Eso es útil, pero es simplemente un caso especial de lo que he descrito anteriormente.Usar
std::begin
y amigos es una idea particularmente buena al escribir código de plantilla, porque esto hace que esas plantillas sean más genéricas. Para los que no son plantillas, también podría usar métodos, cuando corresponda.PD: Soy consciente de que esta publicación tiene casi 7 años. Lo encontré porque quería responder una pregunta que estaba marcada como un duplicado y descubrí que ninguna respuesta aquí menciona ADL.
fuente
Mientras que las funciones que no son miembros no proporcionan ningún beneficio para los contenedores estándar, su uso impone un estilo más consistente y flexible. Si en algún momento desea extender una clase de contenedor no estándar existente, preferiría definir sobrecargas de las funciones libres, en lugar de alterar la definición de la clase existente. Por lo tanto, para los contenedores no estándar son muy útiles y siempre usar las funciones gratuitas hace que su código sea más flexible, ya que puede sustituir el contenedor estándar por un contenedor no estándar más fácilmente y el tipo de contenedor subyacente es más transparente para su código. admite una variedad mucho más amplia de implementaciones de contenedores.
Pero, por supuesto, esto siempre tiene que ser ponderado correctamente y la abstracción tampoco es buena. Aunque el uso de las funciones gratuitas no es una gran abstracción, sin embargo, rompe la compatibilidad con el código C ++ 03, que a esta temprana edad de C ++ 11 podría ser un problema para usted.
fuente
boost::begin()
/end()
, por lo que no hay incompatibilidad real :)begin/end
). Por lo tanto, consideraría que también es una incompatibilidad con C ++ 03 puro. Pero como se dijo, es una incompatibilidad bastante pequeña (y cada vez más pequeña), ya que C ++ 11 (al menosbegin/end
en particular) está recibiendo cada vez más adopción, de todos modos.En última instancia, el beneficio está en el código que se generaliza de modo que sea independiente del contenedor. Puede operar en un
std::vector
, una matriz o un rango sin cambios en el código en sí.Además, los contenedores, incluso los que no son de propiedad, se pueden adaptar para que también se puedan usar de manera independiente mediante código utilizando accesores basados en rangos no miembros.
Ver aquí para más detalles.
fuente