C ++

Expressões Lambda em C ++

Expressões Lambda em C ++

Por que Lambda Expression?

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 lambda. Considere também o seguinte programa curto:

#incluir
usando namespace std;
int fn (int par)

resposta int = par + 3;
resposta de retorno;

int main ()

fn (5);
return 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 lambda. A expressão lambda (função) nessa posição é um prvalue.

Qualquer literal, exceto o literal de string, é um prvalue. A expressão lambda é um design de função especial que caberia como um literal no código. É uma função anônima (sem nome). Este artigo explica a nova expressão primária C ++, chamada de expressão lambda. Conhecimento básico em C ++ é um requisito para entender este artigo.

Conteúdo do Artigo

  • Ilustração da Expressão Lambda
  • Partes da Expressão Lambda
  • Capturas
  • Esquema de função de retorno de chamada clássico com expressão lambda
  • O tipo de retorno à direita
  • Fecho
  • Conclusão

Ilustração da expressão 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)

resposta int = param + 3;
resposta de retorno;
;
int main ()

auto variab = fn (2);
cout << variab << '\n';
return 0;

O resultado é:

    5

Fora da função main (), existe a variável, fn. Seu tipo é automático. Auto nesta situação significa que o tipo real, como int ou float, é determinado pelo operando certo do operador de atribuição (=). À direita do operador de atribuição está uma expressão 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 de fn.

Na função main (), existe a declaração:

    auto variab = fn (2);

Isso significa que fn fora de main () acaba sendo o identificador de uma função. Seus parâmetros implícitos são aqueles da expressão lambda. O tipo para variab é automático.

Observe que a expressão lambda termina com ponto-e-vírgula, assim como a definição de classe ou estrutura, termina com ponto-e-vírgula.

No programa a seguir, uma função, que é uma expressão lambda que retorna o valor 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)

resposta int = param + 3;
resposta de retorno;
);
return 0;

O resultado é:

    4 5

Existem duas funções aqui, a expressão lambda e a função otherfn (). A expressão lambda é o segundo argumento do otherfn (), 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 autônoma).

O parâmetro da função lambda na definição da função otherfn () é um ponteiro para uma função. O ponteiro tem o nome, ptr. O nome, ptr, é usado na definição otherfn () para chamar a função lambda.

A declaração,

    int no2 = (* ptr) (2);

Na definição otherfn (), ele chama a função lambda com um argumento de 2. O valor de retorno da chamada, "(* ptr) (2)" da função lambda, é atribuído a no2.

O programa acima também mostra como a função lambda pode ser usada no esquema de função de retorno de chamada do C ++.

Partes da Expressão Lambda

As partes de uma função lambda típica 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 isolada, ela deve terminar com um ponto e vírgula.

Capturas

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 tal chamada de função deve ter como parâmetro, um ponteiro para uma função, correspondendo à definição da função lambda.

A definição da função lambda é diferente da definição da função normal. Ele pode ser atribuído a uma variável no escopo global; esta função atribuída a variável também pode ser codificada dentro de 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 introdutor lambda, permite que as variáveis ​​sejam enviadas do escopo circundante (função) para o corpo da função da expressão lambda. Diz-se que o corpo da função da expressão 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 lambda. O programa a seguir ilustra isso, com o escopo da função main (), como o escopo circundante:

#incluir
usando namespace std;
int main ()

id int = 5;
auto fn = [id] ()

cout << id << '\n';
;
fn ();
return 0;

A saída é 5. Sem o nome, id, dentro de [], a expressão lambda não teria visto a variável id do escopo da função main ().

Capturando por Referência

O exemplo de uso da cláusula de captura acima é capturar por valor (veja os detalhes abaixo). Na captura 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 irá alterar o valor dessa mesma variável no escopo circundante. Cada variável repetida na cláusula de captura é precedida pelo e comercial (&) para conseguir isso. O programa a seguir ilustra isso:

#incluir
usando namespace std;
int main ()

id int = 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';
return 0;

O resultado é:

    6, 3.4, B

Confirmando que os nomes das variáveis ​​dentro do corpo da função da expressão lambda são para as mesmas variáveis ​​fora da expressão lambda.

Capturando por valor

Na captura 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 conseguir a captura por valor, cada variável repetida na cláusula de captura não é precedida por nada. O programa a seguir ilustra isso:

#incluir
usando namespace std;
int main ()

id int = 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';
return 0;

O resultado é:

5, 2.3, A
6, 3.4, B

Se o indicador de comentário for removido, o programa não irá compilar. O compilador emitirá uma mensagem de erro informando 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, como mostra a saída do programa acima.

Mistura de capturas

A captura por referência e a captura por valor podem ser misturadas, como mostra o programa a seguir:

#incluir
usando namespace std;
int main ()

id int = 5; float ft = 2.3; char ch = 'A'; bool bl = verdadeiro;
auto fn = [id, ft, & ch, & bl] ()

ch = 'B'; bl = falso;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
return 0;

O resultado é:

    5, 2.3, B, 0

Quando todos são capturados, são por referência:

Se todas as variáveis ​​a serem capturadas são capturadas por referência, então apenas uma & será suficiente na cláusula de captura. O programa a seguir ilustra isso:

#incluir
usando namespace std;
int main ()

id int = 5; float ft = 2.3; char ch = 'A'; bool bl = verdadeiro;
auto fn = [&] ()

id = 6; ft = 3.4; ch = 'B'; bl = falso;
;
fn ();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
return 0;

O resultado é:

6, 3.4, B, 0

Se algumas variáveis ​​devem ser capturadas por referência e outras por valor, então um & representará todas as referências, e o resto não será precedido por nada, como mostra o programa a seguir:

usando namespace std;
int main ()

id int = 5; float ft = 2.3; char ch = 'A'; bool bl = verdadeiro;
auto fn = [&, id, ft] ()

ch = 'B'; bl = falso;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
return 0;

O resultado é:

5, 2.3, B, 0

Observe que & sozinho (i.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 devem ser capturadas por valor, então apenas um = será suficiente na cláusula de captura. O programa a seguir ilustra isso:

#incluir
usando namespace std;
int main ()

id int = 5; float ft = 2.3; char ch = 'A'; bool bl = verdadeiro;
auto fn = [=] ()

cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
return 0;

O resultado é:

5, 2.3, A, 1

Observação: = é somente leitura, a partir de agora.

Se algumas variáveis ​​devem ser capturadas por valor e outras por referência, então um = representará todas as variáveis ​​copiadas somente leitura, e o resto terá cada um &, como mostra o programa a seguir:

#incluir
usando namespace std;
int main ()

id int = 5; float ft = 2.3; char ch = 'A'; bool bl = verdadeiro;
auto fn = [=, & ch, & bl] ()

ch = 'B'; bl = falso;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
return 0;

O resultado é:

5, 2.3, B, 0

Observe que = sozinho deve ser o primeiro caractere na cláusula de captura.

Esquema de função de retorno de chamada clássico com expressão lambda

O programa a seguir mostra como um esquema de função de retorno de chamada clássico pode ser feito com a expressão lambda:

#incluir
usando namespace std;
saída char *;
cba automático = [] (caractere fora [])

output = out;
;
void principalFunc (char input [], void (* pt) (char []))

(* pt) (entrada);
cout<<"for principal function"<<'\n';

void fn ()

cout<<"Now"<<'\n';

int main ()

entrada char [] = "para função de retorno de chamada";
principalFunc (entrada, cba);
fn ();
cout<return 0;

O resultado é:

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

Lembre-se de que quando uma definição de expressão lambda é atribuída a uma variável no escopo global, seu corpo de função pode ver as variáveis ​​globais sem empregar a cláusula de captura.

O tipo de retorno à direita

O tipo de retorno de uma expressão lambda é auto, o que significa que o compilador determina o tipo de retorno da expressão de retorno (se presente). Se o programador realmente deseja indicar o tipo de retorno, ele o fará como no seguinte programa:

#incluir
usando namespace std;
auto fn = [] (int param) -> int

resposta int = param + 3;
resposta de retorno;
;
int main ()

auto variab = fn (2);
cout << variab << '\n';
return 0;

A saída é 5. Após a lista de parâmetros, o operador de seta é digitado. Isso é seguido pelo tipo de retorno (int neste caso).

Fecho

Considere o seguinte segmento de código:

struct Cla

id int = 5;
char ch = 'a';
obj1, obj2;

Aqui, Cla é o nome da classe de estrutura.  Obj1 e obj2 são dois objetos que serão instanciados a partir da classe struct. A expressão lambda é semelhante na implementação. A definição da função lambda é um tipo de classe. Quando a função lambda é chamada (invocada), um objeto é instanciado a partir de sua definição. Este objeto é chamado de fechamento. É o fechamento que faz o trabalho que se espera que o lambda faça.

No entanto, codificar a expressão lambda como a estrutura acima terá obj1 e obj2 substituídos pelos argumentos dos parâmetros correspondentes. O programa a seguir ilustra isso:

#incluir
usando namespace std;
auto fn = [] (int param1, int param2)

resposta interna = param1 + param2;
resposta de retorno;
(2, 3);
int main ()

auto var = fn;
cout << var << '\n';
return 0;

A saída é 5. Os argumentos são 2 e 3 entre parênteses. Observe que a chamada de função da expressão lambda, fn, não leva 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. É em duas partes: classe e objeto. Sua definição é uma espécie 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 se espera que o lambda faça.

Para que a expressão lambda receba uma variável de um escopo de função externo, ela precisa de uma cláusula de captura não vazia em seu corpo de função.

Os 5 principais produtos ergonômicos de mouse de computador para Linux
O uso prolongado do computador causa dor no pulso ou nos dedos? Você está sofrendo de rigidez nas articulações e constantemente precisa apertar as mão...
Como alterar as configurações do mouse e touchpad usando Xinput no Linux
A maioria das distribuições Linux vem com a biblioteca “libinput” por padrão para lidar com eventos de entrada em um sistema. Ele pode processar event...
Remapeie os botões do mouse de maneira diferente para software diferente com o controle de botão do mouse X
Talvez você precise de uma ferramenta que possa fazer o controle do seu mouse mudar com cada aplicativo que você usa. Se for esse o caso, você pode ex...