Vivek
El siguiente código se compila con gcc 4.5.1 pero no con VS2010 SP1:
#include <iostream>
#include <vector>
#include <map>
#include <utility>
#include <set>
#include <algorithm>
using namespace std;
class puzzle
{
vector<vector<int>> grid;
map<int,set<int>> groups;
public:
int member_function();
};
int puzzle::member_function()
{
int i;
for_each(groups.cbegin(), groups.cend(), [grid, &i](pair<int,set<int>> group) {
i++;
cout << i << endl;
});
}
Este es el error:
error C3480: 'puzzle::grid': a lambda capture variable must be from an enclosing function scope
warning C4573: the usage of 'puzzle::grid' requires the compiler to capture 'this' but the current default capture mode does not allow it
- ¿Qué compilador es el correcto?
- ¿Cómo puedo usar miembros de datos dentro de una lambda en VS2010?
Xeo
Creo que VS2010 es correcto esta vez, y verificaría si tuviera el estándar a mano, pero actualmente no lo tengo.
Ahora, es exactamente como dice el mensaje de error: no se pueden capturar cosas fuera del alcance de la lambda.† grid
no está en el ámbito de aplicación, pero this
es (cada acceso a grid
en realidad sucede como this->grid
en funciones miembro). Para su caso de uso, capturar this
funciona, ya que lo usará de inmediato y no desea copiar el grid
auto lambda = [this](){ std::cout << grid[0][0] << "\n"; }
Sin embargo, si desea almacenar la cuadrícula y copiarla para acceder a ella posteriormente, donde puzzle
Es posible que el objeto ya esté destruido, deberá hacer una copia local intermedia:
vector<vector<int> > tmp(grid);
auto lambda = [tmp](){}; // capture the local copy per copy
† Estoy simplificando: busque en Google “alcance del alcance” o vea §5.1.2 para todos los detalles sangrientos.
-
Me parece bastante limitado. No entiendo por qué un compilador necesitaría evitar tal cosa. Funciona bien con bind, aunque la sintaxis es horrible con el operador de desplazamiento a la izquierda ostream.
– Jean-Simon Brochu
27 de noviembre de 2013 a las 14:53
-
Podría
tmp
ser unconst &
agrid
reducir las copias? Todavía queremos al menos una copia, la copia en la lambda ([tmp]
), pero sin necesidad de una segunda copia.– Aaron McDaid
28 de julio de 2015 a las 15:05
-
La solución podría hacer una copia extra innecesaria de
grid
aunque probablemente se optimice. Más corto y mejor es:auto& tmp = grid;
etc.– Tom Swirly
10 de agosto de 2015 a las 17:49
-
Si tiene C++ 14 disponible, podría hacerlo
[grid = grid](){ std::cout << grid[0][0] << "\n"; }
para evitar la copia extra– sigi
18 de marzo de 2016 a las 11:49
-
Parece estar arreglado en gcc 4.9 (y gcc 5.4 para el caso)
error: capture of non-variable ‘puzzle::grid’
– BGabor
29 de noviembre de 2019 a las 14:27
Trass3r
Resumen de las alternativas:
captura this
:
auto lambda = [this](){};
use una referencia local al miembro:
auto& tmp = grid;
auto lambda = [ tmp](){}; // capture grid by (a single) copy
auto lambda = [&tmp](){}; // capture grid by ref
C++14:
auto lambda = [ grid = grid](){}; // capture grid by copy
auto lambda = [&grid = grid](){}; // capture grid by ref
ejemplo: https://godbolt.org/g/dEKVGD
-
Es interesante que solo el uso explícito de la captura con la sintaxis del inicializador funcione para esto (es decir, en C ++ 14 simplemente haciendo
[&grid]
todavía no funciona). ¡Muy contento de saber esto!– plata mate
10 de febrero de 2017 a las 15:53
-
Buen resumen. Encuentro la sintaxis de C++14 muy conveniente
– tuket
22 de julio de 2018 a las 12:39
-
Tenga en cuenta que el nombre de la variable puede ser cualquier cosa:
auto lambda = [&anyName = grid](){};
– estrellado
16 de marzo a las 6:05
Creo que necesitas capturar this
.
-
Esto es correcto, capturará el puntero this y aún puede consultar
grid
directamente. El problema es, ¿qué pasa si quieres copiar la cuadrícula? Esto no te permitirá hacer eso.– Xeo
25/10/2011 a las 21:19
-
Puede, pero solo de forma indirecta: debe hacer una copia local y capturar eso en la lambda. Esa es solo la regla con las lambdas, no puede capturar rígido fuera del alcance adjunto.
– Xeo
25/10/2011 a las 21:30
-
Seguro que puedes copiar. Quise decir que no puedes copiarlo y capturarlo, por supuesto.
– Michael Krelin – hacker
25/10/2011 a las 21:40
-
Lo que describí hace una captura de copia, a través de la copia local intermedia; vea mi respuesta. Aparte de eso, no conozco ninguna forma de copiar capturar una variable miembro.
– Xeo
25/10/2011 a las 21:42
-
Claro, copia la captura, pero no el miembro. Se trata de dos copias a menos que el compilador sea más inteligente de lo habitual, supongo.
– Michael Krelin – hacker
25/10/2011 a las 21:52
Un método alternativo que limita el alcance de la lambda en lugar de darle acceso a todo this
es pasar una referencia local a la variable miembro, por ejemplo
auto& localGrid = grid;
int i;
for_each(groups.cbegin(),groups.cend(),[localGrid,&i](pair<int,set<int>> group){
i++;
cout<<i<<endl;
});
Nota: debe ser
pair<const int, set<int> >
, ese es el tipo de par real de un mapa. Posiblemente también debería ser una referencia a const.– Xeo
25/10/2011 a las 21:21
Relacionado; muy útil: thispointer.com/…
– Gabriel grapas
28 de abril de 2020 a las 5:10
usar [&] capturar por referencia.
– herr_azad
26 de noviembre de 2021 a las 23:16