Expressões lambda em c ++

Expressões lambda em c ++

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
  • Partes da expressão lambda
  • Captura
  • Esquema clássico de função de retorno de chamada com expressão lambda
  • O tipo de retorno de direita
  • Fecho
  • Conclusão

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 é:

5

Fora 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 5

Existem 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:

[] ()
  • [] é a cláusula de captura. Pode ter itens.
  • () é para a lista de parâmetros.
  • é para o corpo da função. Se a função estiver sozinha, deve terminar com um ponto de vírgula.

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, b

Confirmando 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, a
6, 3.4, b

Se 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, 0

Quando 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, 0

Se 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, 0

Observe 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, 1

Observaçã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, 0

Observe 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<retornar 0;

A saída é:

para função principal
Agora
para função de retorno de chamada

Lembre -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.