Expressão lambda
Formato de expressão lambda
Expressões lambda em C ++ 11 são usadas para definir e criar objetos de função anônima para simplificar a programação.
A sintaxe do Lambda é a seguinte:
[函数对象参数] (操作符重载函数参数) mutable 或 exception 声明 -> 返回值类型 {函数体}
Formato do código:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
using fptr = int(*)(int& obj);
int main()
{
fptr fobj = [](int& obj)mutable throw(int)->int {return obj; };
}
Como você pode ver, Lambda é dividido principalmente em cinco partes: [parâmetro de objeto de função], (parâmetro de função de sobrecarga do operador), declaração mutável ou de exceção, -> tipo de valor de retorno, {corpo da função}.
Lista de captura: [lista de parâmetros de captura]
Identifica o início de uma expressão Lambda. Esta parte deve existir e não pode ser omitida. Os parâmetros do objeto de função são passados para o construtor da classe de objeto de função gerada automaticamente pelo compilador. Os parâmetros do objeto de função só podem usar as variáveis locais que são visíveis no escopo do Lambda até que o Lambda seja definido (incluindo este da classe onde o Lambda está localizado). Os parâmetros do objeto de função têm os seguintes formatos:
① Vazio: não captura nenhum parâmetro externo, a captura é na verdade "a variável no escopo é usada diretamente no corpo da função";
② =: O corpo da função pode usar todas as variáveis locais visíveis no escopo do Lambda (incluindo esta da classe onde o Lambda está localizado), e é um método de transferência de valor (equivalente ao compilador passando automaticamente todas as variáveis locais por valor para nós );
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [=](int& obj)mutable {obj += a; cout << obj << endl; });
}
Passe uma variável por valor:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [a](int& obj)mutable {obj += a; cout << obj << endl; });
}
Nota:
A função de Mutable é modificar o valor da variável capturada pela lista de captura dentro do corpo da função lambda, mas como eu passo os parâmetros por transferência de valor, mudar a dentro do corpo da função lambda não fará com que o a externo mude.
③ &: O corpo da função pode usar todas as variáveis locais visíveis dentro do escopo do Lambda (incluindo este da classe onde Lambda está localizado), e é passado por referência (equivalente ao compilador passando automaticamente todas as variáveis locais por referência para nós) ;
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [&](int& obj)mutable {obj += a; cout << obj << endl; });
}
Para capturar uma única variável por referência:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
int main()
{
int a = 10;
vector<int> obj{ 1,2,3,4,5 };
for_each(obj.begin(), obj.end(), [&a](int& obj)mutable {obj += a; cout << obj << endl; });
}
④ isto: A variável membro na classe onde Lambda está localizado pode ser usada no corpo da função;
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class Person
{
public:
int age;
string name;
public:
Person(int age, string name)
{
this->age = age;
this->name = name;
}
void ShowInf()
{
function<void()> fptr = [this]() {cout << this->name << "的年龄为" << this->age << endl; };
fptr(); // 调用lambda函数
}
};
int main()
{
Person obj(12, "张三");
obj.ShowInf();
}
Nota:
⑴ O protótipo da expressão lambda aqui é function <return data type (parameter data type)>, que é semelhante ao ponteiro de função na linguagem C. Na verdade, os dois são essencialmente iguais, mas a expressão lambda tem mais " captura apenas "Lista";
⑵ Lembre-se: como passamos este ponteiro, membros estáticos não podem ser chamados na classe!
O seguinte código está correto?
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class Person
{
public:
int age;
string name;
static int mark;
public:
Person(int age, string name)
{
this->age = age;
this->name = name;
}
function<void()> ShowInf()
{
return [this]() {cout << this->name << "的年龄为" << this->age << endl; }; // 返回lambda表达式
}
};
int main()
{
Person obj(12, "张三");
obj.ShowInf();
}
Isto está errado. Vamos explorar por que isso está errado?
O tipo de dados retornado pela expressão lambda no código acima é nulo, portanto, o resultado da expressão lambda de retorno também é nulo.
Novo uso da função Lambda:
#include <iostream>
using namespace std;
#include <algorithm>
#include <vector>
#include <string>
#include <functional>
class Person
{
public:
int age;
string name;
static int mark;
public:
Person(int age, string name)
{
this->age = age;
this->name = name;
}
void ShowInf()
{
function<void()> fptr = [this]() {cout << this->name << "的年龄为" << this->age << endl; };
fptr(); // 调用lambda函数
}
};
int main()
{
Person obj(12, "张三");
obj.ShowInf();
int fptr1 = [&](int obj) {cout << obj << endl; return 0; }(4); // fptr1的数据类型是int整型
cout << typeid(fptr1).name() << endl;
}
Dê uma olhada no código acima, o código a seguir é o mais significativo:
int fptr1 = [&](int obj) {cout << obj << endl; return 0; }(4); // fptr1的数据类型是int整型
fptr é um tipo int, por que isso?
function<int(int)> fptr1 = [&](int obj) {cout << obj << endl; return 0; };
fptr1(4);
O código equivalente é mostrado acima, de fato, o último parêntese da expressão lambda é usado para passar parâmetros.
Mas há um pré-requisito para isso. O tipo de dado retornado pela expressão lambda não pode ser nulo. Para o tipo de dado void, este formulário não está disponível, ou seja, não há variável para receber dados do tipo void.
A forma de uma expressão lambda cujo tipo de dados de valor de retorno é nulo é a seguinte:
[&](int obj) {cout << obj << endl; }(9);
⑤ a, & b: Passe a por valor eb por referência;
⑥ =, & a, & b. Exceto que aeb são passados por referência, outros parâmetros são passados por valor;
⑦ &, a, b. Exceto para a e b que são passados por valor, outros parâmetros são passados por referência.
Variáveis que não precisam declarar tipos de dados podem ser definidas na lista de captura e usadas no corpo da função lambda:
Lista de parâmetros: (lista de parâmetros de função)
Identifica o parâmetro do operador sobrecarregado (). Se não houver parâmetro, esta parte pode ser omitida. Os parâmetros podem ser passados por valor (como: (a, b)) e por referência (como: (& a, & b)).
declaração mutável ou exceção
Esta parte pode ser omitida. Ao passar parâmetros de objeto de função por valor, após adicionar o modificador mutável, a cópia passada pode ser modificada (observe que a cópia pode ser modificada, não o próprio valor). A declaração de exceção é usada para especificar a exceção lançada pela função, como lançar uma exceção de tipo inteiro, você pode usar throw (int).
-> Tipo de valor de retorno
Identifica o tipo do valor de retorno da função. Quando o valor de retorno é nulo ou há apenas um retorno no corpo da função (o compilador pode inferir automaticamente o tipo de valor de retorno neste momento), esta parte pode ser omitida.
{Corpo da função}
Identifica a realização da função. Esta parte não pode ser omitida, mas o corpo da função pode estar vazio.
Instancie funções anônimas
Armazene funções anônimas em variáveis, matrizes ou vetores e os transmita como parâmetros nomeados:
#include <iostream>
using namespace std;
#include <functional>
#include <vector>
int main()
{
function<void(int, int)> fptr = [](int obj1, int obj2) {cout << obj1 + obj2 << endl; }; // 必须参数类型一一对应
fptr(1, 1);
vector<function<void(int, int)>> obj{ fptr };
obj[0](1, 2);
function<void(int, int)> fptr_array[1]{ fptr };
fptr_array[0](4, 6);
}
Função anônima lambda e conversão mútua de ponteiro de função
#include <iostream>
using namespace std;
#include <functional>
using f_ptr = void(*)(int, int);
int main()
{
function<void(int, int)> fptr = [](int obj1, int obj2) {cout << obj1 + obj2 << endl; };
f_ptr fptr1 = [](int obj1, int obj2) {cout << obj1 + obj2 << endl; };
//f_ptr = fptr; // 不存在function<void(int,int)>对象转化至void(*)(int, int)对象
fptr1(1, 3);
}
Observação: como as expressões lambda são mais avançadas do que os ponteiros de função, ou seja, há uma função adicional de "captura de dados externos". Portanto, quando atribuímos expressões lambda a ponteiros de função do tipo correspondente, a lista de captura [] deve ser aérea !