Lenguajes de Programación

Qué son las funciones lambdas en C++

En este artículo tratamos qué son las funciones lambda en C++, que fueron introducidas a partir de la versión 11, además de ver de forma práctica cómo utilizarlas y la gran utilidad que ofrecen las mismas.

Publicado el 01 de Octubre de 2018
Compartir

Qué son las funciones lambda en C++

A partir del estándar de C++ 11 se introdujeron las llamadas funciones lambda, que son funciones anónimas que son creadas en tiempo de ejecución y no en tiempo de compilación.

Cuando programamos una función normal en C++, estas funciones son generadas en tiempo de compilación, por ejemplo:

void foo()
{
    std::cout << “foo”;
}

A partir del estándar 11 de C++, se añadieron un nuevo tipo de funciones, que son generadas en tiempo de ejecución, es decir, cuando el código llega y se está ejecutando, es en ese momento cuando se crea la función.

Sintaxis de las funciones lambda en C++

Una función lambda está englobada por unos corchetes con los parámetros de captura y después, como cualquier otra función, tiene entre paréntesis sus parámetros normales. Además tiene un cuerpo, en el que podemos realizar operaciones.

Vamos a ver un ejemplo:

int main() {
    auto f = [](int& a) -> int {
        return a + 10;
    };
    return 0;
}

Aquí estamos creando una función que recibe un entero y devuelve un entero también.

Si queremos marcarle el tipo de devolución, después de los parámetros, con una flecha podemos marcarle el tipo, como hacemos en el ejemplo.

Hasta aquí todo normal, es decir, los parámetros y los parámetros de retorno pueden ser exactamente los mismos que los de una función normal, y en su cuerpo podemos hacer exactamente lo mismo que en una función normal.

¿Qué diferencia a las funciones lambda de las funciones normales?

Las diferencias son, sobre todo, los parámetros de captura, que son muy útiles.

Con estos parámetros de captura, las funciones lambda pueden capturar todo su entorno, todo el scope en el en el que han sido declaradas, y tomarlas para su cuerpo.

Ejemplos prácticos

Vamos a ver unos ejemplos de cómo utilizar las funciones lambda y sus ventajas.

int main() {
    int b = 10;
    int c = 100;
    auto f = [b, c](int& a) -> int {
        return a + 10;
    };
    return 0;
}

En este ejemplo queremos utilizar b y c para el cómputo, por lo que podemos ponerlos directamente en el cuerpo de captura.

Esto hace que, en el scope en el que está la función, busque esos dos identificadores de variables y copie todos sus parámetros dentro del scope de la función lambda.

Si en lugar de querer que los copie queremos que sea una referencia, vamos a necesitar poner el operador de referencia.

int main() {
    int b = 10;
    int c = 100;
    auto f = [&b, &c](int& a) -> int {
        return a + 10;
    };
    return 0;
}

En este caso, en lugar vez de tener una referencia, de tener una copia de b y c, vamos a tener una referencia.

Además podemos indicar que nos capture todo el scope por referencia.

int main() {
    int b = 10;
    int c = 100;
    auto f = [&](int& a) -> int {
        return a + 10;
    };
    return 0;
}

De esta forma, todo el scope que esté fuera de la función lambda, todo el scope en la que haya sido declarada, tendrá acceso a él por referencia.

De la misma forma, si queremos que sea por copia, podemos acceder a él cambiando el & por un =, con lo que nos quedaría de esta forma:

int main() {
    int b = 10;
    int c = 100;
    auto f = [=](int& a) -> int {
        return a + 10;
    };
    return 0;
}

Vamos a comprobar que todo lo explicado anteriormente funciona.

int main() {
    int b = 10;
    int c = 100;
    auto f = [&](int& a) -> int {
        b += 1;
        c +=1;
        return a + b + c;
    };
    int a = 10;
    std::cout << f(a) << std::endl;
    std::cout << b << std::endl;
    std::cout << c << std::endl;
    return 0;
}

En este caso indicamos que nos capture todo por referencia, que nos devuelva a + b + c y que imprima el resultado y además el valor de b y c.

Al ejecutarlo podemos ver los valores resultantes, que son 122, 11 y 101.

Esto es muy útil por ejemplo cuando estamos trabajando con threads. En el curso de programación genérica y programación concurrente de C ++, en el que se habla de ellos, podéis comprobar en los ejemplos como es muy útil a la hora de trabajar con cosas y compartir memoria entre threads.

Y además de todo lo anterior, vamos a ver otro ejemplo:

class Foo
{
public:
    int m_attr;
    void foo()
    {
        auto f = [this](){};
            m_attr;
        };
    };
};

En este caso tenemos una clase Foo, queremos devolver una función que tenga acceso a los parámetros de la clase, además creamos una función lambda y en el cuerpo de captura podemos indicar que capture el this. Con lo cual, inmediatamente, en la función lambda que estamos declarando, si tenemos un atributo, en el cuerpo de la función lambda podemos acceder directamente a esos atributos.

Esto es muy interesante para hacer wrappers, para enviar datos a threads y compartir los datos entre ellos, o para generar nuestras propias factorías de funciones, que se modifiquen dependiendo del scope o dependiendo de la funcionalidad que nosotros queramos.


Compartir este post

También te puede interesar...

Introducción a C++

Curso de introducción a C++

1 hora y 56 minutos · Curso

¿Quieres aprender a programar en C++? Con este curso de introducción aprenderás desde cero este lenguaje de programación tan versátil.

  • Lenguajes de programación
Tecnología

Por qué aprender C++

06 Septiembre 2018 Antonio José Checa Bustos
Artículos
Ver todos