2011-09-03 11 views
5

Per esempio:Perché non esiste una funzione membro for_each per ogni tipo di raccolta in stl?

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

se molto più elegante e leggibile rispetto al comunemente usato:

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

C'è un legittimo motivo di tale funzione membro non è presente nella norma?

+0

perché si richiederlo? le funzioni membro servono solo allo scopo se l'implementazione può essere resa più efficiente (set :: find è più efficiente di std :: find() su un set).Oh, e se vuoi evitare onnipresente '.begin()', en '.end()' chiama, usa [Boost Range Algorithms] (http://www.boost.org/doc/libs/1_47_0/libs/ gamma/doc/html/gamma/riferimento/algoritmi/introduction.html). Zucchero sintattico dolce – sehe

+0

@sehe: o * less * efficient, come 'std :: list :: sort()' :-) –

+0

@Kerrek: 'std :: list :: sort' è speciale,' std :: sort' richiede 'RandomAccessIterator', quindi non potrebbe funzionare per gli elenchi. –

risposta

8

Questo è il motivo di progettazione standard per l'intera libreria: contenitori separati dagli algoritmi.

Se lo facevi a modo tuo, dovresti implementare ogni funzione X per ogni contenitore Y, portandoti a implementazioni M * N se disponi di funzioni M e N contenitori.

Utilizzando gli iteratori e gli algoritmi di elaborazione funzionano su iteratori anziché su contenitori, è necessario implementare solo algoritmi M più interfacce iteratore.

Questa separazione significa anche che hai infinitamente più ampio campo di applicazione: gli algoritmi non possono semplicemente essere utilizzati per ogni contenitore libreria, ma per qualsiasi contenitore, presente o futuro, che qualcuno decide di scrivere e attrezzare con iteratori . Il riutilizzo finito e infinito è un argomento abbastanza forte! E chiamare gli algoritmi attraverso l'interfaccia generica e gratuita non aggiunge alcun costo.

+0

I mixaggi possono essere utilizzati per alleviare questo problema: è possibile scrivere un mixin di funzionalità che è generico * e * funziona su qualsiasi contenitore. – Puppy

+0

@DeadMG: Come utilizzeresti il ​​missaggio per eseguire una for-one su, ad esempio, una raccolta di corrispondenze di espressioni regolari o attraversamento di directory? –

+0

Il contenitore eredita dal mixin, che utilizza gli iteratori sotto il cofano per fornire la funzionalità. Finché l'oggetto fornisce iteratori, esattamente come fanno ora, il mixin può convertire tale supporto in funzioni membro semplici con l'interfaccia mostrata nell'OP. Inoltre, in realtà è * più * generico perché puoi sovrascrivere la funzionalità nella classe contenitore se devi ad esempio, 'std :: list'. – Puppy

1

Il semplice fatto è che il design della libreria standard deriva da un momento in cui il linguaggio non offriva molte funzionalità e molti progetti comuni, come i mixin basati su CRTP, non erano disponibili. Ciò significa che i design superiori che sono ovvi ora semplicemente non erano implementabili o progettabili quando è stata creata la libreria Standard.

Gli iteratori sono un'ottima implementazione generica, ma creano un'interfaccia generica. Trovo rattristante che invece di risolvere il problema con il design della libreria e di revisionarlo, invece hanno introdotto una funzione di caso speciale per un piccolo sottoinsieme del problema.

+0

Potresti mostrare un piccolo esempio di mixin che può adattarsi a qualsiasi contenitore futuro e al futuro algoritmo? –

+0

@Kerrek: non è necessario. Possiamo semplicemente utilizzare il supporto esistente per gli algoritmi futuri, i contenitori futuri possono ereditarci e possiamo ottenere qualcosa di meglio per gli algoritmi più utilizzati. – Puppy

+0

Capisco. Bene, sarebbe bello offrire questo. Sembra che questo possa essere aggiunto come un set di funzionalità opzionale ... –

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

Come si può vedere for_each prende Iterator ingresso come parametro, in modo che qualsiasi contenitore STL che può fornire un iteratore di input compatibile (cioè a parte iteratore di ingresso, potrebbe anche essere bidirezionale, ad accesso casuale iteratore etc) sarebbe compatibile con std :: for_each. Progettando in questo modo, stl algoritmo separato generico dal tipo di dati (contenitori) che è più elegante generico &.

0

Perché lo richiederebbe?

Le funzioni membro servono solo se l'implementazione può essere resa più efficiente (set :: find è più efficiente di std :: find() su un set).

PS Oh, e se si vuole evitare onnipresente .begin(), en .end() chiamate, utilizzare Boost Range Algorithms. Dolce zucchero sintattico

comunque A Gamma Boost campione ispirato:

#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, ", ")); 
} 
Problemi correlati