C++11 introduziu expressões lambda, que são uma maneira conveniente de definir e usar objetos de função anônimos. Uma expressão lambda é semelhante a uma função normal, pois também possui uma lista de parâmetros, um tipo de valor de retorno e um corpo de função, exceto que é definida de forma mais concisa e pode ser definida dentro de uma função.
A sintaxe de uma expressão lambda é a seguinte:
[capture] (parameters) -> return_type { function_body }
em:
capture
É a lista de captura, que especifica quais variáveis externas podem ser usadas no corpo da função lambda.parameters
é uma lista de parâmetros, semelhante à lista de parâmetros de uma função normal.return_type
é o tipo de valor de retorno, que pode ser omitido e inferido pelo compilador.function_body
É o corpo da função, que contém a lógica específica da função lambda.
Primeiro, olhe para esta pergunta da entrevista
int v = 19;
auto f1 = [v]{return v;}; // 值捕获
auto f2 = [=]{return v;}; // 隐式捕获值
auto f3 = [&v]{return v;}; // 引用捕获
auto f4 = [&]{return v;}; // 隐式捕获引用
v = 10;
std::cout << f1() << " " << f2() << " " << f3() << " " << f4() << std::endl;
O resultado desta questão é 19 19 10 10.
A captura de referência transfere a referência, e a referência também mudará depois que o valor for modificado posteriormente. A captura de valor transfere o valor neste momento e a modificação subsequente não afeta o valor atual.
Existem três maneiras de especificar captura de valor, captura de referência e captura implícita na lista de captura:
- Captura de valor: capture variáveis externas por valor e use uma cópia da variável externa dentro da função lambda. Por exemplo,
[x]
significa capturar variáveis externas por valorx
. Abaixo está um exemplo:
#include <iostream>
int main() {
int x = 10;
auto f = [x]() { std::cout << x << std::endl; };
x = 20;
f(); // 输出 10
return 0;
}
No código acima, capturamos a variável externa por valor x
e exibimos seu valor dentro da função lambda. Como é capturado por valor, mesmo que x
o valor seja modificado posteriormente, o valor original ainda é usado dentro da função lambda.
- Captura de referência: as variáveis externas são capturadas por referência e as próprias variáveis externas são usadas dentro da função lambda. Por exemplo,
[&x]
significa capturar variáveis externas por referênciax
. Abaixo está um exemplo:
#include <iostream>
int main() {
int x = 10;
auto f = [&x]() { std::cout << x << std::endl; };
x = 20;
f(); // 输出 20
return 0;
}
No código acima, capturamos a variável externa por referência x
e exibimos seu valor dentro da função lambda. Como é capturado por referência, quando x
o valor for modificado posteriormente, o novo valor também será utilizado dentro da função lambda.
- Captura implícita: você pode usar
&
ou=
para especificar a captura implícita de todas as variáveis externas. Por exemplo,[&]
significa que todas as variáveis externas são capturadas implicitamente por referência[=]
e todas as variáveis externas são capturadas implicitamente por valor. Abaixo está um exemplo:
#include <iostream>
int main() {
int x = 10, y = 20;
auto f1 = [&]() { std::cout << x << " " << y << std::endl; };
auto f2 = [=]() { std::cout << x << " " << y << std::endl; };
x = 30; y = 40;
f1(); // 输出 30 40
f2(); // 输出 10 20
return 0;
}
No código acima, definimos duas funções lambda usando captura de referência implícita e captura de valor implícito, respectivamente. x
Quando o valor de e é modificado posteriormente y
, a primeira função lambda gera o novo valor, enquanto a segunda função lambda gera o valor original.
Deve-se observar que, ao usar a captura de referência, é necessário garantir que o objeto referenciado ainda exista quando a expressão lambda for executada. Além disso, ao modificar variáveis locais não estáticas capturadas por valor em uma função lambda, palavras-chave precisam ser adicionadas após a lista de parâmetros mutable
.
As expressões lambda são muito flexíveis e poderosas, mas há alguns problemas a serem observados. Por exemplo,
- Preste atenção aos problemas de segurança do encadeamento ao usar a captura de referência em um ambiente multiencadeado;
- Tenha cuidado para não capturar acidentalmente variáveis desnecessárias ao usar a captura implícita;
- Ao usar a captura de valor, preste atenção se a variável capturada pode ser copiada, etc.
Concluindo, considere cuidadosamente várias situações ao usar expressões lambda para evitar problemas inesperados.