Función Lambda de C ++ 11

Este artículo se reproduce de https://www.cnblogs.com/WindSun/p/11182276.html
C ++ 11 agrega una nueva función lambda, y su formato básico es el siguiente:

[捕捉列表] (参数) mutable -> 返回值类型 {函数体}

Descripción:

  • [] es el líder de lambda. La lista de captura puede capturar variables en el contexto para que las utilicen las funciones lambda:
        [var] significa capturar variables mediante transferencia de valor. var
        [=] significa que la transferencia de valor captura todas las variables de alcance principales
        [& var] significa capturar la variable var mediante transferencia de referencia
        [&] significa capturar todas las variables de alcance principal mediante transferencia de referencia
        [esto] significa capturar el puntero actual
       mediante transferencia de valor También hay algunas combinaciones:
        [=, & a] significa capturar un , valor por referencia Método de paso para capturar otras variables
       Nota: La
       lista de captura no permite la transmisión repetida de variables, como: [=, a], [&, & this], lo que provocará errores durante la compilación
  • La lista de parámetros es coherente con la lista de parámetros de una función normal. Si no necesita pasar parámetros, puede vincularlo con () [omitido].
  • mutable puede cancelar el atributo constante de Lambda, porque Lambda es un atributo constante de forma predeterminada; multable solo permite que el cuerpo de la función Lambda modifique la variable pasada por el valor, pero la modificación no afectará a la variable externa.
  • -> Si el tipo de retorno es nulo, puede conectar -> [omitido] juntos. Si el tipo de retorno es claro, puede omitirlo y dejar que el compilador infiera automáticamente el tipo.
  • El cuerpo de la función es el mismo que el de una función normal. Además de los parámetros, también puede utilizar variables capturadas.

La función Lambda más simple:

[]{
    
    }

Ejemplo de uso:

#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    
    
    int a = 5, b = 7;
    auto total = [](int x, int y)->int {
    
    return x + y; };    //接受两个参数
    cout << total(a, b) << endl;  //12
    
    auto fun1 = [=] {
    
    return a + b; };   //值传递捕捉父作用域变量
    cout << fun1() << endl; //12
    
    auto fun2 = [&](int c) {
    
    b = a + c; a = 1; };    //省略了返回值类型,引用捕获所有
    fun2(3);    
    cout << a <<" "<< b << endl; //1 8
    
    a = 5; b = 7;   //被修改后,重新赋值
    auto fun3 = [=, &b](int c) mutable {
    
    b = a + c; a = 1; };    //以值传递捕捉的变量,在函数体里如果要修改,要加mutaple,因为默认const修饰
    fun3(3);
    cout << a << " " << b << endl;    //5,8
    
    a = 5; b = 7;   //被修改后,重新赋值
    auto fun4 = [=](int x, int y) mutable->int {
    
    a += x; b += y; return a + b; };
    int t = fun4(10, 20);
    cout << t << endl;  //42
    cout << fun4(10, 20) << endl;  //72
    cout << a <<" "<< b << endl;    //5 7
    
    return 0;
}

La lista de captura de funciones Lambda fuera del alcance del bloque debe estar vacía, por lo que dichas funciones no son muy diferentes de las funciones ordinarias, excepto por la diferencia de sintaxis.

Las funciones de Lambda dentro del alcance del bloque solo pueden capturar variables automáticas dentro del alcance del bloque, y cualquier variable que no sea de alcance o no automática (variables estáticas) que no estén en este alcance hará que el compilador informe un error.

Función lambda y estática en línea

La función Lambda puede omitir la función en línea estática declarada externamente, que es equivalente a una función local. Las funciones locales solo pertenecen al ámbito principal. En comparación con las funciones en línea estáticas externas o las macros personalizadas, las funciones de Lambda no tienen ninguna ventaja de rendimiento (pero no peor) en tiempo de ejecución, pero las funciones de Lambda son más legibles.
Una vez finalizada la función principal, la función Lambda ya no está disponible y no contaminará ningún espacio de nombres.

Acerca de la captura por valor y mutable

Como se mencionó anteriormente, mutable puede cancelar el atributo constante de Lambda. Si desea modificar el valor en el dominio de la función, debe agregar mutable.
Primer ejemplo:

#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    
    
    int j = 12;
    auto by_val = [=] {
    
    return j + 1; };
    auto by_ref = [&] {
    
    return j + 1; };
    cout << by_val() << endl;   //13
    cout << by_ref() << endl;   //13
    j++;
    cout << by_val() << endl;   //13
    cout << by_ref() << endl;   //14
    
    return 0;
}

En el ejemplo anterior, el resultado de llamar a la transferencia de valor después de j ++ sigue siendo 12. La razón es que la transferencia de valor j se considera una constante, una vez inicializada, no se volverá a modificar (se puede considerar como una constante con la misma nombre como j en el ámbito principal), y en la captura de referencia, j sigue siendo el valor en el ámbito principal.

De hecho, después de convertir un Lambda de paso de valor en una función, se convertirá en una función miembro constante de una clase. El código es básicamente el siguiente:

class const_val_lambda
{
    
    
public:
    const_val_lambda(int v):val(v){
    
    }
public:
    void operator()()const {
    
     val = 3; } //报错
private:
    int val;
};

Pero usar el método de referencia no reportará un error, porque no cambiará la referencia en sí, solo el valor de la referencia

Para ser precisos, el equivalente de lambda en el estándar C ++ 11 existente es un funtor con operador constante O. Por lo tanto, al usar la lista de captura, debe prestar atención al hecho de que las variables capturadas por el método de paso de valor son constantes inmutables en la función lambda. Este diseño del estándar puede derivarse de algunas fallas de diseño en el algoritmo STL temprano (no hay restricciones en el functor, lo que conduce a errores en algunos algoritmos que no están particularmente bien diseñados). Hablando en términos más generales, tal diseño es razonable y cambiar las variables temporales copiadas del contexto generalmente no tiene ningún sentido. La mayoría de las veces, las variables temporales solo se usan como entrada para la función lambda. Si necesitamos enviar el resultado al contexto, podemos usar una referencia o dejar que la función lambda devuelva un valor. Además, el modificador mutable de la función lambda puede eliminar su constancia, pero esto en realidad solo proporciona una posibilidad gramatical.En realidad, no debería haber muchos lugares donde se necesite usar una función lambda mutable. La mayoría de las veces, usamos la función lambda de la versión predeterminada (no mutable) que es suficiente.

Funciones lambda y punteros de función

Las funciones Lambda no son simples tipos de puntero de función o tipos personalizados; cada función Lambda genera un objeto temporal (rvalue) de tipo de cierre. Pero C ++ 11 permite la conversión de funciones Lambda en punteros de función, siempre que:

  • Lambda no captura ninguna variable
  • El prototipo de función que muestra el puntero de función debe tener el mismo método de llamada que Lambda
#include <iostream>

using namespace std;

int main(int argc, char* argv[])
{
    
    
    int a = 3, b = 4;

    auto total = [](int x, int y)->int {
    
    return x + y; };
    typedef int(*all)(int x, int y);
    typedef int(*one)(int x);

    all p;
    p = total;
    //one q;
    //q = total;  //报错,参数不一致

    decltype(total) all_1 = total;
    //decltype(total) all_2 = p;  //报错,指针无法转换为Lambda

    return 0;
}

Lambda y STL

Desde C ++ 11, Lambda se ha utilizado ampliamente en STL, como foreach. En comparación con los punteros de función, los punteros de función tienen grandes defectos:

  1. La función está definida en otro lugar, lo que es difícil de leer;
  2. Es probable que el uso de punteros de función haga que el compilador no realice la optimización en línea en ellos. Cuando el número de bucles es demasiado grande, la brecha de rendimiento entre los punteros de función y Lambdas es demasiado grande. Los punteros de función 2 no se pueden usar en algunos estados que se pueden determinar en tiempo de ejecución.Cuando no hay C ++ 11, solo se pueden usar funciones de functor. Esto reduce en gran medida el costo de aprender algoritmos STL.
      Pero Lambda no es un reemplazo completo de functors. Debido a la limitación de la lista de captura de Lambda, solo se pueden capturar las variables del dominio de efectos secundarios. Las funciones de lanzamiento se comparten de forma inherente entre los ámbitos.

Supongo que te gusta

Origin blog.csdn.net/qq_24649627/article/details/110803657
Recomendado
Clasificación