2015-06-26 19 views
23

Entrambi possono essere utilizzati per applicare una funzione a un intervallo di elementi.Qual è la differenza tra std :: transform e std :: for_each?

A livello alto:

  • std::for_each ignora il valore restituito della funzione e ordine di esecuzione garantisce.
  • std::transform assegna il valore di ritorno all'iteratore e non garantisce l'ordine di esecuzione .

Quando preferisci utilizzare l'uno rispetto all'altro? Ci sono dei caveat sottili?

+7

'transform' ha un campo di uscita,' for_each' non lo fa. – user657267

+4

Un sacco di '' consiste in funzioni molto simili, hanno solo una * leggera * differenza. – o11c

+0

@ o11c questo è esattamente quello che sto cercando. Ci deve essere una buona ragione per la leggera differenza. – bendervader

risposta

27

std::transform è lo stesso di map. L'idea è di applicare una funzione a ciascun elemento tra i due iteratori e ottenere un contenitore diverso composto da elementi risultanti dall'applicazione di tale funzione. Potresti usarlo per, per esempio, proiettare il membro dei dati di un oggetto in un nuovo contenitore. Nel seguito, std::transform viene utilizzato per trasformare un contenitore di std::string s in un contenitore di std::size_t s.

std::vector<std::string> names = {"hi", "test", "foo"}; 
std::vector<std::size_t> name_sizes; 

std::transform(names.begin(), names.end(), std::back_inserter(name_sizes), [](const std::string& name) { return name.size();}); 

D'altra parte, si esegue std::for_each per gli effetti collaterali unici. In altre parole, std::for_each ricorda da vicino un ciclo for basato su intervallo.

Indietro esempio stringa:

std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) { 
    std::cout << name_size << std::endl; 
}); 

Infatti, partendo da C++ 11 stesso può essere ottenuto con una notazione terser utilizzando gamma basata for loop:

for (std::size_t name_size: name_sizes) { 
    std::cout << name_size << std::endl; 
} 
+2

per estendere su questo, in C++ 14 possiamo rimuovere il tipo dal campo di base per il ciclo di essere ora: for (name_size: name_sizes) {...} –

+0

bello! l'ultima volta che ho controllato era solo una proposta. Generalmente uso 'for (auto && x: xs)', ma quel livello di astrazione non era veramente cruciale per la spiegazione. –

+0

@TrevorHickey Ho controllato rapidamente e non riesco a trovare nessuna notizia a parte la proposta originale N3853. Sei sicuro di questo? –

0

reale esempio di utilizzo di std :: trasformata di è quando si desidera convertire una stringa in maiuscolo, è possibile scrivere codice come questo:

std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper); 

se si cercherà di raggiungere stessa cosa con std :: for_each come:

std::for_each(s.begin(), s.end(), ::toupper); 

E 'abitudine convertirlo in stringa di caratteri maiuscoli

12

tuo high livello panoramica

  • std::for_each ignora il valore di ritorno della funzione e garantisce l'ordine di esecuzione.
  • std::transform assegna il valore di ritorno all'iteratore e non garantisce l'ordine di esecuzione.

praticamente lo copre.

Un altro modo di guardarlo (preferire uno sopra l'altro);

  • I risultati (il valore restituito) dell'operazione sono importanti?
  • L'operazione su ciascun elemento è un metodo membro senza valore di ritorno?
  • Esistono due intervalli di input?

Una cosa da tenere a mente (sottile avvertimento) è il cambiamento nei requisiti delle operazioni di std::transform prima e dopo C++ 11 (da en.cppreference.com);

  • Prima C++ 11, sono stati richiesti a "non avere effetti collaterali",
  • Dopo C++ 11, questo cambiato in "non deve invalidare alcuna iteratori, inclusi i iteratori estremità, o modificare qualsiasi elemento delle gamme coinvolte "

Sostanzialmente dovevano consentire l'ordine di esecuzione indeterminato.

Quando posso utilizzare uno sopra l'altro?

Se voglio manipolare ciascun elemento in un intervallo, quindi uso for_each. Se devo calcolare qualcosa da ogni elemento, allora userei transform. Quando si utilizzano for_each e transform, normalmente li accoppio con un lambda.

Detto questo, trovo il mio uso corrente del tradizionale for_each essere diminuita un po 'dopo l'avvento della gamma basato for loop e lambda in C++ 11 (for (element : range)). Trovo la sua sintassi e l'implementazione molto naturali (ma il tuo chilometraggio qui varierà) e un adattamento più intuitivo per alcuni casi d'uso.

+0

potresti approfondire un po 'il motivo per cui "std :: transform ... non garantisce l'ordine di esecuzione"? – athos

+1

Questo è in contrasto con l'algoritmo 'for_each', è necessario applicare la funzione a ciascun elemento nell'ordine. I requisiti delle operazioni di trasformazione per non avere effetti collaterali significano che l'ordine di esecuzione non sarebbe osservabile, quindi non garantito. Ciò detto, le implementazioni che ho esaminato lo applicano nell'ordine atteso, prima alla fine. – Niall

8

Anche se la questione è stata risolta, credo che questo esempio potrebbe chiarire ulteriormente la differenza.

for_each appartiene a operazioni STL non modificabili, il che significa che queste operazioni non modificano gli elementi della raccolta o la raccolta stessa. Pertanto, il valore restituito da for_each viene sempre ignorato e non viene assegnato a un elemento di raccolta. Tuttavia, è ancora possibile modificare gli elementi della raccolta, ad esempio quando un elemento viene passato alla funzione f mediante riferimento. Si dovrebbe evitare tale comportamento in quanto non è coerente con i principi STL.

Al contrario, transform funzione appartiene operazioni modificare STL e 'applicato dato predicati (unary_op o binary_op) agli elementi della raccolta o collezioni e memorizzare i risultati in un'altra raccolta.

#include <vector> 
#include <iostream> 
#include <algorithm> 
#include <functional> 
using namespace std; 

void printer(int i) { 
     cout << i << ", "; 
} 
int main() { 
    int mynumbers[] = { 1, 2, 3, 4 }; 
    vector<int> v(mynumbers, mynumbers + 4); 

    for_each(v.begin(), v.end(), negate<int>());//no effect as returned value of UnaryFunction negate() is ignored. 
    for_each(v.begin(), v.end(), printer);  //guarantees order 

    cout << endl; 

    transform(v.begin(), v.end(), v.begin(), negate<int>());//negates elements correctly 
    for_each(v.begin(), v.end(), printer); 
    return 0; 
} 

che stamperà:

1, 2, 3, 4, 
-1, -2, -3, -4, 
+0

È sbagliato dire nessun effetto. Ha un effetto, cioè nega gli interi. Tuttavia, non è memorizzato in memoria. –

+0

@JossieCalderon Ho modificato la mia risposta, spero che sia ora più chiara. – BugShotGG

Problemi correlati