Por que expressão de lambda?
Considere a seguinte declaração:
int myint = 52;
Aqui, myint é um identificador, um lvalue. 52 é um literal, um prvalue. Hoje, é possível codificar uma função especialmente e colocá -la na posição de 52. Essa função é chamada de expressão de lambda. Considere também o seguinte programa curto:
#incluir
usando namespace std;
int fn (int par)
int resposta = par + 3;
resposta de retorno;
int main ()
fn (5);
retornar 0;
Hoje, é possível codificar uma função especialmente e colocá -la na posição do argumento de 5, da chamada de função, fn (5). Essa função é chamada de expressão de lambda. A expressão lambda (função) nessa posição é um prvalue.
Qualquer literal, exceto que a corda literal é um prvalue. A expressão lambda é um design de função especial que se encaixaria como literal em código. É uma função anônima (sem nome). Este artigo explica a nova expressão primária C ++, chamada expressão de lambda. Conhecimento básico em C ++ é um requisito para entender este artigo.
Conteúdo do artigo
Ilustração da expressão de lambda
No programa a seguir, uma função, que é uma expressão lambda, é atribuída a uma variável:
#incluir
usando namespace std;
Auto fn = [] (int param)
int resposta = param + 3;
resposta de retorno;
;
int main ()
Auto variab = fn (2);
cout << variab << '\n';
retornar 0;
A saída é:
5Fora da função principal (), existe a variável, fn. Seu tipo é automático. Auto nesta situação significa que o tipo real, como int ou flutuante, é determinado pelo operando direito do operador de atribuição (=). À direita do operador de tarefas está uma expressão de lambda. Uma expressão lambda é uma função sem o tipo de retorno anterior. Observe o uso e a posição dos colchetes, []. A função retorna 5, um int, que determinará o tipo para FN.
Na função Main (), há a afirmação:
Auto variab = fn (2);
Isso significa que FN fora do main () acaba como o identificador para uma função. Seus parâmetros implícitos são os da expressão lambda. O tipo para variab é automático.
Observe que a expressão de Lambda termina com um semicolon, assim como a definição de classe ou estrutura, termina com um semicolon.
No programa a seguir, uma função, que é uma expressão de Lambda que retorna o valor de 5, é um argumento para outra função:
#incluir
usando namespace std;
void Otherfn (int no1, int (*ptr) (int))
int no2 = (*ptr) (2);
cout << no1 << " << no2 << '\n';
int main ()
Otherfn (4, [] (int param)
int resposta = param + 3;
resposta de retorno;
);
retornar 0;
A saída é:
4 5Existem duas funções aqui, a expressão lambda e a função outro. A expressão lambda é o segundo argumento do outrofn (), chamado em main (). Observe que a função Lambda (expressão) não termina com um ponto e vírgula nesta chamada porque, aqui, é um argumento (não uma função independente).
O parâmetro da função lambda na definição da função outrofn () é um ponteiro para uma função. O ponteiro tem o nome, PTR. O nome, PTR, é usado na definição de outrofn () para chamar a função lambda.
A declaração,
int no2 = (*ptr) (2);
Na definição de outrofn (), ela chama a função lambda com um argumento de 2. O valor de retorno da chamada, "(*ptr) (2)" da função Lambda, é atribuído ao NO2.
O programa acima também mostra como a função Lambda pode ser usada no esquema de função de retorno de chamada C ++.
Partes da expressão lambda
As partes de uma função típica lambda são as seguintes:
[] ()
Captura
A definição da função lambda pode ser atribuída a uma variável ou usada como argumento para uma chamada de função diferente. A definição para essa chamada de função deve ter como parâmetro, um ponteiro para uma função, correspondente à definição da função lambda.
A definição de função lambda é diferente da definição de função normal. Pode ser atribuído a uma variável no escopo global; Esta função assignada a variável também pode ser codificada em outra função. Quando atribuído a uma variável de escopo global, seu corpo pode ver outras variáveis no escopo global. Quando atribuído a uma variável dentro de uma definição de função normal, seu corpo pode ver outras variáveis no escopo da função apenas com a ajuda da cláusula de captura, [].
A cláusula de captura [], também conhecida como o lambda-introdutor, permite que as variáveis sejam enviadas do escopo circundante (função) no corpo da função da expressão de Lambda. Diz -se que o corpo da função da expressão de Lambda captura a variável quando recebe o objeto. Sem a cláusula de captura [], uma variável não pode ser enviada do escopo circundante para o corpo da função da expressão de Lambda. O programa a seguir ilustra isso, com o escopo da função principal (), como o escopo circundante:
#incluir
usando namespace std;
int main ()
int id = 5;
Auto fn = [id] ()
cout << id << '\n';
;
fn ();
retornar 0;
A saída é 5. Sem o nome, id, dentro [], a expressão lambda não teria visto o ID variável do escopo da função principal ().
Captura por referência
O exemplo acima do uso da cláusula de captura é capturar por valor (veja os detalhes abaixo). Ao capturar por referência, a localização (armazenamento) da variável, e.g., Id acima, do escopo circundante, é disponibilizado dentro do corpo da função Lambda. Portanto, alterar o valor da variável dentro do corpo da função lambda mudará o valor da mesma variável no escopo circundante. Cada variável repetida na cláusula de captura é precedida pelo AMPERSAND (&) para conseguir isso. O programa a seguir ilustra o seguinte:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a';
Auto fn = [& id, & ft, & ch] ()
id = 6; ft = 3.4; ch = 'b';
;
fn ();
cout << id << ", " << ft << ", " << ch << '\n';
retornar 0;
A saída é:
6, 3.4, bConfirmando que os nomes variáveis dentro do corpo da função da expressão de Lambda são para as mesmas variáveis fora da expressão de lambda.
Captura por valor
Ao capturar por valor, uma cópia da localização da variável, do escopo circundante, é disponibilizada dentro do corpo da função Lambda. Embora a variável dentro do corpo da função lambda seja uma cópia, seu valor não pode ser alterado dentro do corpo a partir de agora. Para alcançar a captura por valor, cada variável repetida na cláusula de captura não é precedida por nada. O programa a seguir ilustra o seguinte:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a';
Auto fn = [id, ft, ch] ()
// id = 6; ft = 3.4; ch = 'b';
cout << id << ", " << ft << ", " << ch << '\n';
;
fn ();
id = 6; ft = 3.4; ch = 'b';
cout << id << ", " << ft << ", " << ch << '\n';
retornar 0;
A saída é:
5, 2.3, aSe o indicador de comentário for removido, o programa não compilará. O compilador emitirá uma mensagem de erro de que as variáveis dentro da definição do corpo da função da expressão lambda não podem ser alteradas. Embora as variáveis não possam ser alteradas dentro da função Lambda, elas podem ser alteradas fora da função Lambda, conforme a saída do programa acima mostra.
Capturas de mistura
Captura por referência e captura por valor pode ser misturada, como mostra o seguinte programa:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [id, ft, & ch, & bl] ()
ch = 'b'; bl = false;
cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
retornar 0;
A saída é:
5, 2.3, b, 0Quando todos capturados, são por referência:
Se todas as variáveis a serem capturadas forem capturadas por referência, apenas uma e será suficiente na cláusula de captura. O programa a seguir ilustra o seguinte:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [&] ()
id = 6; ft = 3.4; ch = 'b'; bl = false;
;
fn ();
cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
retornar 0;
A saída é:
6, 3.4, b, 0Se algumas variáveis forem capturadas por referência e outras por valor, então uma e representará todas as referências, e o restante não será precedido por nada, como mostra o seguinte programa:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [&, id, ft] ()
ch = 'b'; bl = false;
cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
retornar 0;
A saída é:
5, 2.3, b, 0Observe isso e sozinho (eu.e., e não seguido por um identificador) deve ser o primeiro caractere na cláusula de captura.
Quando todos capturados, são por valor:
Se todas as variáveis a serem capturadas forem capturadas por valor, então apenas um = será suficiente na cláusula de captura. O programa a seguir ilustra o seguinte:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [=] ()
cout << id << ", " <<
ft << ", " << ch <<
"," << bl << '\n';
;
fn ();
retornar 0;
A saída é:
5, 2.3, a, 1Observação: = é somente leitura, a partir de agora.
Se algumas variáveis forem capturadas pelo valor e outras por referência, então um = representará todas as variáveis copiadas somente leitura, e o restante terá e, como mostra o seguinte programa:
#incluir
usando namespace std;
int main ()
int id = 5; float ft = 2.3; char ch = 'a'; bool bl = true;
Auto fn = [=, & ch, & bl] ()
ch = 'b'; bl = false;
cout << id << ", " << ft <<
"," << ch << ", " <<
bl << '\n';
;
fn ();
retornar 0;
A saída é:
5, 2.3, b, 0Observe que = sozinho deve ser o primeiro caractere na cláusula de captura.
Esquema clássico de função de retorno de chamada com expressão lambda
O programa a seguir mostra como um esquema de função de retorno de chamada clássica pode ser feito com a expressão Lambda:
#incluir
usando namespace std;
Char *saída;
CBA automático = [] (char out [])
saída = out;
;
Void PrincipalfUncunc (Char Input [], void (*pt) (char []))
(*pt) (entrada);
cout<<"for principal function"<<'\n';
void fn ()
cout<<"Now"<<'\n';
int main ()
char input [] = "para função de retorno de chamada";
Principalfald (entrada, CBA);
fn ();
cout<
A saída é:
para função principalLembre -se de que, quando uma definição de expressão de Lambda é atribuída a uma variável no escopo global, seu corpo de função pode ver variáveis globais sem empregar a cláusula de captura.
O tipo de retorno de direita
O tipo de retorno de uma expressão de lambda é automático, o que significa que o compilador determina o tipo de retorno da expressão de retorno (se presente). Se o programador realmente quiser indicar o tipo de retorno, ele fará isso como no programa a seguir:
#incluir
usando namespace std;
Auto fn = [] (int param) -> int
int resposta = param + 3;
resposta de retorno;
;
int main ()
Auto variab = fn (2);
cout << variab << '\n';
retornar 0;
A saída é 5. Após a lista de parâmetros, o operador de seta é digitado. Isto é seguido pelo tipo de retorno (int neste caso).
Fecho
Considere o seguinte segmento de código:
struct cla
int id = 5;
char ch = 'a';
obj1, obj2;
Aqui, CLA é o nome da classe STRUT. OBJ1 e OBJ2 são dois objetos que serão instanciados da classe STRUT. A expressão lambda é semelhante na implementação. A definição de função lambda é um tipo de classe. Quando a função lambda é chamada (invocada), um objeto é instanciado de sua definição. Este objeto é chamado de fechamento. É o fechamento que faz o trabalho que o lambda deve fazer.
No entanto, codificar a expressão de lambda como a estrutura acima terá obj1 e obj2 substituído pelos argumentos dos parâmetros correspondentes. O programa a seguir ilustra o seguinte:
#incluir
usando namespace std;
Auto fn = [] (int param1, int param2)
int resposta = param1 + param2;
resposta de retorno;
(2, 3);
int main ()
Auto var = fn;
cout << var << '\n';
retornar 0;
A saída é 5. Os argumentos são 2 e 3 entre parênteses. Observe que a chamada da função da expressão de Lambda, FN, não assume nenhum argumento, pois os argumentos já foram codificados no final da definição da função lambda.
Conclusão
A expressão lambda é uma função anônima. Está em duas partes: classe e objeto. Sua definição é um tipo de classe. Quando a expressão é chamada, um objeto é formado a partir da definição. Este objeto é chamado de fechamento. É o fechamento que faz o trabalho que o lambda deve fazer. Para que a expressão lambda receba uma variável de um escopo de função externa, ele precisa de uma cláusula de captura não vazia em seu corpo de função.