2011-08-17 2 views
24

Sto facendo una semplice normalizzazione su un vettore (pesi), cercando di fare uso di algoritmi STL per rendere il codice più pulito possibile (mi rendo conto che questo è abbastanza banale con cicli for):Come posso accedere alle variabili locali da una funzione anonima C++ 11?

float tot = std::accumulate(weights.begin(), weights.end(), 0.0); 
std::transform(weights.begin(), weights.end(), [](float x)->float{return(x/tot);}); 

Al momento, tot non è visibile alla funzione anonima, quindi questo non viene compilato. Qual è il modo migliore per rendere una variabile locale visibile alla funzione anonima?

+0

scusa, 0 dovrebbe essere stato 0.0! modificato – bd1

risposta

39

Hai bisogno di una chiusura.

float tot = std::accumulate(weights.begin(), weights.end(), 0); 
std::transform(weights.begin(), weights.end(), [tot](float x)->float{return(x/tot);}); 

In questo caso, tot viene catturato dal valore. 11 lambda C++ supporto cattura da:

  1. valore [x]
  2. riferimento [&x]
  3. qualsiasi variabile attualmente portata mediante riferimento [&]
  4. stesso come 3, ma in valore [=]

È possibile mescolare uno dei precedenti in un elenco separato da virgole [x, &y].

+0

Grande! C'è un buon riferimento web/book sull'uso della funzione anonima in C++ 11 in STL? Molto di ciò che ho trovato sul Web è costituito da soluzioni alternative obsolete per funzioni anonime o post di blog casuali. – bd1

+1

@ bd1 Il wikipedia su C++ 0x è in realtà abbastanza buono. Anche l'ultima bozza standard disponibile al pubblico è chiamata n3424. Sfortunatamente non ci sono ancora libri su C++ 0x. – pmr

+0

Se si effettua una chiusura con '[=]' e fa copie di _all_ le variabili in ambito di inclusione, ciò include le variabili globali? Quando smette di salire sulla scala dell'oscilloscopio? –

2

è necessario aggiungere tot alla "lista di cattura":

float tot = std::accumulate(weights.begin(), weights.end(), 0); 
std::transform(weights.begin(), weights.end(), [tot](float x)->float{return(x/tot);}); 

In alternativa è possibile utilizzare un cattura-default per catturare tot implicitamente:

float tot = std::accumulate(weights.begin(), weights.end(), 0); 
std::transform(weights.begin(), weights.end(), [=](float x)->float{return(x/tot);}); 
8

Il lambda possono variabili "catturare" dall'ambito ambientale:

[ ..., N, ... ](int a, int b) -> int { return (a + b) * N; } 
^^^^^^^^^^^^^ ^^^^^^^^^^^^  ^^^^ 
captured vars local params  ret.type 

È possibile capt ure in base al valore o per riferimento e puoi utilizzare la sintassi speciale [=] e [&] per acquisire qualsiasi cosa dall'ambito ambientale, ad esempio qualsiasi cosa tu abbia effettivamente utilizzato.

+0

Cosa succede se sto analizzando un lambda come parametro di funzione. Genera un errore dicendo che la funzione lambda non è il tipo giusto per il parametro. – Acidic

+0

@Acidic: poiché non è possibile pronunciare il * tipo * di un'espressione lambda, è molto difficile avere * funzioni * il cui parametro è un tipo di chiusura. In genere, si utilizza la funzione * templates * per dedurre il tipo o un wrapper per la cancellazione del tipo come 'std :: function' se è necessario gestire raccolte eterogenee di callables. –