Todas as funções lambdas apresentadas no post anterior não possuiam membro de dados. A utilização de membro de dados pode ser feita por meio da captura de variáveis locais que é realizada pelo introdutor lambda ([]). Neste introdutor é possível especificar uma lista de captura (capture-list) como demonstrado no Código 1.
Código 1
int main()
{
vectorv;
for (int i = 0; i < 10; ++i)
v.push_back(i);
int x = 0;
int y = 0;
cout << "Input: ";
cin >> x >> y;
v.erase(remove_if(v.begin(), v.end(), [x, y](int n) { return x < n && n < y; }), v.end());
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
}
Um exemplo da execução do programa do Código 1 seria
Input: 4 7
0 1 2 3 4 7 8 9
É importante salientar que apesar do corpo da função lambda estar lexicamente no escopo de main() , ela esta conceitualmente fora do escopo de main(). Sendo assim, não é possível usar variáveis locais declaradas em main() sem “captura-las”.
Todas as capturas são feitas por valor e apenas cópias das variáveis são armazenadas na função. Isso implica em algumas conseqüências importantes: (a) não é possível modificar as cópias capturadas, pois por default a chamada da função objeto é const, (b) atualizações nas variáveis locais não serão refletidas nas cópias capturadas. Mais a frente neste post irei explicar como capturar variáveis por referência.
Ao invés de informar todas as variáveis que deseja capturar, é possível especificar a captura de todas as variáveis locais. A syntax para isso é o introdutor lambda [=]. Usando o exemplo do código 1, bastaria substituir a linha 14 para:
v.erase(remove_if(v.begin(), v.end(), [=](int n) { return x < n && n < y; }), v.end());
Usando este recurso, o compilador irá fazer a captura por valor de todas as variáveis de main() (no exemplo x e y)
Caso seja necessário modificar as variáveis capturadas, pode-se modificar a chamada da função lambda de const para non-const. Para isso deve-se usar o já conhecido modificador mutable. O Código 2 mostra um exemplo deste recurso.
Código 2
int main()
{
vectorv;
for (int i = 0; i < 10; ++i)
v.push_back(i);
int x = 1;
int y = 1;
for_each(v.begin(), v.end(), [=](int& r) mutable {
const int old = r;
r *= x * y;
x = y;
y = old;
});
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
cout << x << ", " << y << endl;
}
O exemplo mostrado no Código 2 multiplica cada elemento de v com os 2 elementos anteriores.
Sua execução mostraria
0 0 0 6 24 60 120 210 336 504
1, 1
É importante ressaltar que modificações nas variáveis capturadas não são refletidas nas variáveis locais. Caso seja necessário refletir as modificações das variáveis capturadas sobre suas correspondentes locais, isso é alcançado por meio da captura por referência. A syntax para a captura por referência é o introdutor lambda [&x, &y, &z]. A declaração anterior deve ser lida como referência para x, y e z e não endereço de x, y e z. O Código 3 mostra o Código 2 modificado para usar variáveis capturadas por referência.
Código 3
int main() {
vectorv;
for (int i = 0; i < 10; ++i)
v.push_back(i);
int x = 1;
int y = 1;
for_each(v.begin(), v.end(), [&x, &y](int& r) {
const int old = r;
r *= x * y;
x = y;
y = old;
});
for_each(v.begin(), v.end(), [](int n) { cout << n << " "; });
cout << endl;
cout << x << ", " << y << endl;
}
O exemplo mostrado no Código 3 multiplica cada elemento de v com os 2 elementos anteriores.
A execução do Código 3 mostraria
0 0 0 6 24 60 120 210 336 504
8, 9
Observe as diferenças do Código 3 com o Código 2: (i) O introdutor lambda [&x, &y], (ii) a retirada do modificador mutable e (iii) as variáveis locais tendo valores 8 e 9 ao final da execução, refletindo as modificações dentro da função lambda. Também é possível capturar todas as variáveis locais por referência. usando o introdutor lambda [&]
Finaliza aqui o papo sobre funções lambdas. Acho que para um post introdutório o que já foi dito ate aqui é suficiente. Tem muito mais coisas interessantes sobre funções lambdas que não abordei aqui. Para um estudo mais extensivo sobre esta funcionalidade recomendo a leitura da última versão da especificação que pode ser encontrado aqui.
Irei continuar postando sobre o C++ 0x. Percebi que a quantidade de material na língua portuguesa é mínima e irei tentar dar minha contribuição.
[]’s
Leonardo X. T. Cardoso