2011-09-03 12 views
5

Por ejemplo:¿Por qué no hay una función para cada miembro para cada tipo de colección en stl?

v.for_each([](int i) { printf("%d\n", i); }); 

si mucho más elegante y fácil de leer que el comúnmente utilizado:

std::for_each(v.begin(), v.end(), [](int i) { printf("%d\n", i); }); 

¿Existe una razón legítima tal función miembro no se encuentra en la norma?

+0

por qué habría que así lo requieran? las funciones miembro solo sirven para el propósito si la implementación se puede hacer más eficiente (set :: find es más eficiente que std :: find() en un conjunto).Ah, y si quiere evitar las omnipresentes llamadas '.begin()', en '.end()', use [Boost Range Algorithms] (http://www.boost.org/doc/libs/1_47_0/libs/ range/doc/html/range/reference/algorithms/introduction.html). Dulce azúcar sintáctico – sehe

+0

@sehe: o * menos * eficiente, como 'std :: list :: sort()' :-) –

+0

@Kerrek: 'std :: list :: sort' es especial,' std :: sort' requiere 'RandomAccessIterator', por lo que no podría funcionar para las listas. –

Respuesta

8

Esta es la lógica de diseño estándar para toda la biblioteca: Separa los contenedores de los algoritmos.

Si lo hiciste a tu manera, tendrías que implementar cada característica X para cada contenedor Y, lo que te llevaría a las implementaciones M * N si tienes M características y N contenedores.

Al utilizar iteradores y hacer que los algoritmos funcionen en iteradores en lugar de contenedores, solo tiene que implementar algoritmos M más N interfaces de iterador.

Esta separación también significa que tiene alcance infinitamente más amplio de aplicación: los algoritmos no sólo pueden ser utilizados para cada contenedor biblioteca, pero para cualquier contenedor, presente o futuro, que cualquier persona decide escribir y equipar con iteradores . La reutilización finita vs infinita es un argumento bastante fuerte. Y llamar a los algoritmos a través de la interfaz genérica gratuita no agrega ningún costo.

+0

Mixins se puede utilizar para aliviar este problema; puede escribir una combinación de funciones que sea genérica * y * funcione en cualquier contenedor. – Puppy

+0

@DeadMG: ¿Cómo usarías la mezcla para hacer un for-each en, digamos, una colección de coincidencias de expresiones regulares o de recorrido de directorios? –

+0

El contenedor hereda de la mezcla, que usa iteradores debajo del capó para proporcionar la funcionalidad. Siempre que el objeto proporcione iteradores, exactamente como lo hace ahora, el mixin puede convertir ese soporte en funciones miembro simples con la interfaz que se muestra en el OP. Además, en realidad es * más * genérico porque puede anular la funcionalidad en la clase de contenedor usted mismo si necesita, por ejemplo, 'std :: list'. – Puppy

1

El hecho simple es que el diseño de la biblioteca estándar es de una época en la que el lenguaje no ofrecía muchas características, y muchos diseños comunes ahora, como las mixins basadas en CRTP, no existían. Esto significa que los diseños superiores que ahora son obvios simplemente no se podían implementar o designar cuando se creó la biblioteca estándar.

Los iteradores son una gran implementación genérica, pero hacen una interfaz genérica sucky. Me entristece que, en lugar de resolver el problema con el diseño de la biblioteca y revisarlo, introdujeran una función de lenguaje de caso especial para un pequeño subconjunto del problema.

+0

¿Podría mostrar un pequeño ejemplo de mixin que pueda adaptarse a cualquier futuro contenedor y algoritmo futuro? –

+0

@Kerrek: No tiene por qué. Solo podemos usar el soporte existente para futuros algoritmos, los contenedores futuros pueden simplemente heredar de él, y podemos obtener algo mejor para los algoritmos más utilizados. – Puppy

+0

Ya veo. Bueno, sería bueno ofrecer esto. Parece que esto podría agregarse como un conjunto de características opcionales ... –

2
template <class InputIterator, class UnaryFunction> 
UnaryFunction for_each(InputIterator first, InputIterator last, UnaryFunction f); 

Como se puede ver for_each toma iterador de entrada como en el parámetro, por lo que cualquier contenedor STL que puede proporcionar un iterador de entrada compatible (es decir, aparte de iterador de entrada, sino que también podría ser el acceso bidireccional, al azar iterador etc) sería compatible con std :: for_each. Al diseñar de esta manera, stl algoritmo genérico separado del tipo de datos (contenedores) que es más elegante & genérico.

0

¿Por qué lo necesitarías?

Las funciones de miembro solo sirven al propósito si la implementación se puede hacer más eficiente (set :: find es más eficiente que std :: find() en un conjunto).

PS Ah, y si se quiere evitar ubicua .begin(), en .end() llamadas, utilizar Boost Range Algorithms. dulce de azúcar sintáctica

Una gama aleatoria Boost muestra inspirada:

#include <boost/range/adaptors.hpp> 
#include <boost/range/algorithm.hpp> 
#include <boost/pending/integer_range.hpp> 

using namespace boost::adaptors; 

static int mod7(int v) 
    { return v % 7; } 

int main() 
{ 
    std::vector<int> v; 

    boost::copy(
      boost::make_integer_range(1,100) | transformed(mod7), 
      std::back_inserter(v)); 

    boost::sort(v); 

    boost::copy(
      v | reversed | uniqued, 
      std::ostream_iterator<int>(std::cout, ", ")); 
} 
Cuestiones relacionadas