2009-04-09 7 views
6

Sto utilizzando molto il codicecon std::for_each, bind e così via, ma ho notato che a volte l'utilizzo di STL non è una buona idea.Quanto di STL è troppo?

Per esempio, se si dispone di un std::vector e volete fare una sola azione su ciascun elemento del vettore, la prima idea è quella di utilizzare questo:

std::for_each(vec.begin(), vec.end(), Foo()) 

ed è elegante e ok, per un po '. Ma poi arriva la prima serie di segnalazioni di bug e devi modificare il codice. Ora si dovrebbe aggiungere il parametro di chiamare Foo(), così ora diventa:

std::for_each(vec.begin(), vec.end(), std::bind2nd(Foo(), X)) 

ma che è solo soluzione temporanea. Ora il progetto sta maturando e comprendi molto meglio la logica di business e vuoi aggiungere nuove modifiche al codice. È a questo punto che ti rendi conto che dovresti usare il vecchio bene:

for(std::vector::iterator it = vec.begin(); it != vec.end(); ++it) 

Questo sta accadendo solo a me? Riconosci questo tipo di pattern nel tuo codice? Hai riscontrato anti-pattern simili usando STL?

+1

BTW, se nel tuo secondo esempio, "X" è uguale per tutti gli elementi. Basta passarlo al costruttore di Foo e conservarlo nel funtore invece di legarlo. Molto più semplice –

+0

Stai effettivamente utilizzando nessuno degli STL, quindi sei al sicuro :) –

+0

Solo un'opinione, ma "troppo" è quando usi funzionalità che non ti servono solo per il gusto di usarle. Se è utile, usalo. Se sei preoccupato per nomi troppo lunghi, puoi sempre usare un alias typedef o namespace per accorciarli (ad esempio, typedef std :: vector :: iterator VecIter; '). La cosa con la libreria standard è, di solito è il modo più generico per fare qualcosa su un determinato compilatore, semplicemente perché i creatori del compilatore o le persone che lo conoscono meglio (se non i creatori) scriveranno la sua implementazione. Non aver paura di usarlo quando è utile. –

risposta

10

L'utilizzo di boost::bind con std :: for_each risolve questo problema in modo pulito. Oppure puoi usare BOOST_FOREACH.

Esempio di std :: for_each:

std::for_each(v.begin(), v.end(), boost::bind(&C::f, _1, param)); 

Esempio di BOOST_FOREACH:

std::list<int> list_int(/*...*/); 
BOOST_FOREACH(int i, list_int) 
{ 
    // do something with i 
} 
+0

... fino ad arrivare a 10 parametri, cioè! :) – Kylotan

+1

a questo punto li raggruppate all'interno di una struttura :) –

+2

No, a questo punto riconsiderate l'architettura che richiede una funzione per avere 10 parametri;) –

3

Simile al tuo problema, ho spesso notato che il "funtore" modello/linguaggio in C++ è in realtà abbastanza ingombrante. Ecco perché non vedo l'ora di Lambda Functions in C++ 0X. Alcuni di questi sono possibili con boost :: lambda ora.

+0

OMG, la sintassi ... 0X ha 8 mesi in più fino a quando non termina più di 3+ ​​anni fino a quando i principali compilatori non si aggiornano. Sarà molto tempo. –

+1

@Anton: è già in g ++ e Visual Studio 10 (RC disponibile gratuitamente ora) –

10

Può andare anche nella direzione opposta. Supponiamo di iniziare con un'operazione che richiede solo un paio di righe. Non si vuole perdere tempo la creazione di una funzione che verrà chiamato solo una volta, giusto per condensare il ciclo, in modo da scrivere qualcosa di simile:

for() 
{ 
    // do 
    // some 
    // stuff 
} 

Poi, come l'operazione è necessario eseguire diventa più complesso, ci si rende conto che tirando in una funzione separata ha un senso così si finisce con

for() 
    do_alot_more_stuff(); 

E poi modificarlo per essere come il vostro metodo originale ha un senso per condensare ulteriormente verso il basso:

std::for_each(begin, end, do_alot_more_stuff); 

Alla fine, quanto è difficile cambiare davvero un for_each in un ciclo for, o viceversa? Non abbatterti per piccoli dettagli!

5

Utilizzare come qualsiasi altro strumento di lingua. Quando ti semplifica la vita, usalo. Quando diventa ingombrante, fai qualcos'altro. Non è come se fosse davvero difficile refactoring un ciclo in un modo o nell'altro, quando i requisiti cambiano.

+0

Esattamente. Cosa c'è di sbagliato nel cambiare codice? –

0

Pensa anche a Parallelismo, con una funzione puoi definire cosa cambierà e indicare se un intervallo di elementi può invece essere fatto in Parallela piuttosto 1 alla volta dall'inizio alla fine.

1

Forse si è utilizzato for_each invece di trasformare, in primo luogo ...

1

non ho mai utilizzare std :: for_each (o molto di rado).

Suggerisco di utilizzare Boost.Foreach e le costruzioni classiche "per" per ora. E quando C++ 0x è fuori, potresti prendere in considerazione l'utilizzo del nuovo costrutto "for", reso più leggibile per iterare attraverso i contenitori.

+1

Non vedo l'ora che il C++ ottenga un vero ciclo "for" (piuttosto che il ciclo glorififed che ha ora). –

0

Oppure si potrebbe aspettare per C++ 0x e utilizzare for(elem& e, container){e.something();}
proprio la stessa BOOST_FOREACH(), ma fa parte dello standard (in alcuni anni ...).

2

Ho avuto lo stesso problema con un sacco di cose in Algorithm. Ha una brutta tendenza a finire con lo altro codice che usa solo un ciclo per vecchio stile.

Non posso davvero giustificare l'avvio e la creazione di qualche speciale classe functor (che è un argomento moderatamente avanzato in C++ che molti dei miei manutentori non comprenderanno appieno) con un costruttore e un distruttore appropriati, e forse alcuni accessor, semplicemente per evitare un loop per una riga.