Expressões Lambda em C ++

Lambda Expressions C



Por que a expressão Lambda?

Considere a seguinte declaração:

intmyInt= 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 namespacehoras;

intfn(intAtravés dos)

{

intresponder=Através dos+ 3;

Retornaresponder;

}


inta Principal()

{

fn(5);



Retorna 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

No programa a seguir, uma função, que é uma expressão lambda, é atribuída a uma variável:





#incluir

usando namespacehoras;

autofn= [](intparam)

{

intresponder=param+ 3;

Retornaresponder;

};


inta Principal()

{

autovariab=fn(2);

custo <<variab<< ' n';


Retorna 0;

}

O resultado é:

5

Fora da função main (), existe a variável, fn. Seu tipo é automático. Nesta situação, Auto 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:

autovariab=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 de 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 namespacehoras;

vaziootherfn(intno1,int (*ptr)(int))

{

intno2= (*ptr)(2);

custo <<no1<< '' <<no2<< ' n';

}


inta Principal()

{

otherfn(4,[](intparam)

{

intresponder=param+ 3;

Retornaresponder;

});


Retorna 0;
}

O resultado é:

Quatro cinco

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

intno2= (*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 namespacehoras;

inta Principal()

{

intEu iria= 5;


autofn= [Eu iria]()

{

custo <<Eu iria<< ' n';

};

fn();


Retorna 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 uso do exemplo acima da cláusula de captura é a captura por valor (veja os detalhes abaixo). Na captura por referência, a localização (armazenamento) da variável, por exemplo, id acima, do escopo circundante, é disponibilizada dentro do corpo da função lambda. Portanto, mudar o valor da variável dentro do corpo da função lambda mudará o valor dessa mesma variável no escopo circundante. Cada variável repetida na cláusula de captura é precedida pelo e comercial (&) para isso. O programa a seguir ilustra isso:

#incluir

usando namespacehoras;

inta Principal()

{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA';

autofn= [EEu iria,Eft,ECH]()

{

Eu iria= 6;ft= 3,4;CH= 'B';

};

fn();

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ' n';

Retorna 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 namespacehoras;

inta Principal()

{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA';

autofn= [id, ft, ch]()

{

// id = 6; ft = 3,4; ch = 'B';

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ' n';

};

fn();

Eu iria= 6;ft= 3,4;CH= 'B';

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ' n';

Retorna 0;

}

O resultado é:

5, 2,3, A

6, 3,4, B

Se o indicador de comentário for removido, o programa não será compilado. 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, 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 seguinte programa:

#incluir

usando namespacehoras;

inta Principal()

{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA'; boolbl= verdade;


autofn= [id, ft,ECH,Ebl]()

{

CH= 'B';bl= falso;

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ',' <<bl<< ' n';

};

fn();


Retorna 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 um & será suficiente na cláusula de captura. O programa a seguir ilustra isso:

#incluir

usando namespacehoras;

inta Principal()

{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA'; boolbl= verdade;


autofn= [E]()

{

Eu iria= 6;ft= 3,4;CH= 'B';bl= falso;

};

fn();

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ',' <<bl<< ' n';


Retorna 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 seguinte programa:

usando namespacehoras;

inta Principal()

{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA'; boolbl= verdade;


autofn= [E, id, ft]()

{

CH= 'B';bl= falso;

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ',' <<bl<< ' n';

};

fn();


Retorna 0;

}

O resultado é:

5, 2,3, B, 0

Observe que & sozinho (ou seja, & 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 namespacehoras;

inta Principal()
{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA'; boolbl= verdade;


autofn= [=]()

{

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ',' <<bl<< ' n';

};

fn();


Retorna 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 namespacehoras;

inta Principal()

{

intEu iria= 5; flutuadorft= 2,3; CaracteresCH= 'PARA'; boolbl= verdade;


autofn= [=,ECH,Ebl]()

{

CH= 'B';bl= falso;

custo <<Eu iria<< ',' <<ft<< ',' <<CH<< ',' <<bl<< ' n';

};

fn();


Retorna 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 namespacehoras;

Caracteres *saída;


autocba= [](CaracteresFora[])

{

saída=Fora;

};



vaziomainFunc(Caracteresentrada[],vazio (*para)(Caracteres[]))

{

(*para)(entrada);

custo<<'para função principal'<<' n';

}


vaziofn()

{

custo<<'Agora'<<' n';

}


inta Principal()

{

Caracteresentrada[] = 'para função de retorno de chamada';

mainFunc(entrada, cba);

fn();

custo<<saída<<' n';



Retorna 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 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 houver). Se o programador realmente deseja indicar o tipo de retorno, ele o fará como no seguinte programa:

#incluir

usando namespacehoras;

autofn= [](intparam) -> int

{

intresponder=param+ 3;

Retornaresponder;

};


inta Principal()

{

autovariab=fn(2);

custo <<variab<< ' n';


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

estruturaCla

{

intEu iria= 5;

CaracteresCH= 'para';

}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 namespacehoras;

autofn= [](intparam1,intparam2)

{

intresponder=param1+param2;

Retornaresponder;

} (2,3);


inta Principal()

{

autoOnde=fn;

custo <<Onde<< ' n';


Retorna 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 aceita 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. É dividido 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.