2011-08-17 19 views
24

que estoy haciendo una normalización simple en un vector (pesos), tratando de hacer uso de algoritmos de STL para hacer el código más limpio posible (me di cuenta que esto es bastante trivial con los bucles):¿Cómo puedo acceder a las variables locales desde dentro de una función anónima de C++ 11?

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

Actualmente, tot no es visible para la función anónima, por lo que no se compila. ¿Cuál es la mejor manera de hacer que una variable local sea visible para la función anónima?

+0

lo siento, 0 debería haber sido 0.0! editado – bd1

Respuesta

39

Necesita un cierre.

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

En este caso, tot se captura por valor. C++ 11 lambdas soporte de captura por:

  1. valor [x]
  2. referencia [&x]
  3. cualquier variable actualmente en su alcance por referencia [&]
  4. mismo que 3, pero por valor [=]

Puede mezclar cualquiera de los anteriores en una lista separada por comas [x, &y].

+0

¡Genial! ¿Existe una buena referencia web/libro sobre el uso de funciones anónimas de C++ 11 en STL? Mucho de lo que encontré en la web fueron soluciones obsoletas para funciones anónimas o publicaciones aleatorias de blogs. – bd1

+1

@ bd1 La wikipedia en C++ 0x es realmente bastante buena. Además, el último borrador estándar disponible para el público se llama n3424. Lamentablemente todavía no hay libros en C++ 0x. – pmr

+0

Si realiza un cierre con '[=]' y realiza copias de _todas_ las variables en el alcance adjunto, ¿eso incluye variables globales? ¿Cuándo deja de subir en la escala de alcance? –

2

es necesario agregar tot a la "lista de captura":

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

Alternativamente, puede utilizar una captura-default para capturar tot implícitamente:

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

La lambda variables pueden "capturar" del ámbito de aplicación ambiente:

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

Puede capturar por valor o por referencia, y puede usar la sintaxis especial [=] y [&] para capturar cualquier cosa del alcance del entorno, es decir, cualquier cosa que realmente termine usando.

+0

¿Qué ocurre si estoy analizando un lambda como parámetro de función? Lanza un error que dice que la función lambda no es del tipo correcto para el parámetro. – Acidic

+0

@Acidic: Como no puede decir el * tipo * de una expresión lambda, es muy difícil tener * funciones * cuyo parámetro sea un tipo de cierre. Normalmente, debe utilizar la función * templates * para deducir el tipo, o una envoltura de borrado de tipo como 'std :: function' si necesita administrar colecciones heterogéneas de callables. –

Cuestiones relacionadas