What is a lambda expression?

Dominando las Expresiones Lambda en C++

27/06/2023

Valoración: 3.95 (10090 votos)

Desde la introducción del estándar C++11, una de las características más potentes y flexibles que ha transformado la forma en que escribimos código son las expresiones lambda. Estas construcciones permiten definir funciones anónimas directamente en el lugar donde se necesitan, lo que resulta en un código más conciso, legible y expresivo. Si alguna vez te has encontrado escribiendo pequeñas funciones o functores solo para pasarlos como argumento a un algoritmo de la STL, las lambdas son la solución que estabas esperando.

What are the applications of lambda expressions in C++?
Following are the common applications of lambda expressions in C++ Inline, Anonymous Functions: Write small functions directly where needed without naming them. Callbacks and Event Handling: Use lambdas as callbacks for asynchronous operations or event handlers.

En este artículo, exploraremos en profundidad qué son las expresiones lambda, desglosaremos su sintaxis, entenderemos el poderoso mecanismo de captura de variables y veremos ejemplos prácticos que te ayudarán a integrarlas eficazmente en tus proyectos de C++.

Índice de Contenido

¿Qué es Exactamente una Expresión Lambda?

Una expresión lambda, a menudo llamada simplemente "lambda", es una forma de crear una función anónima en línea. "Anónima" significa que no tiene un nombre como las funciones tradicionales que se definen con void miFuncion(). En su lugar, se define un objeto de función, conocido como closure o functor anónimo, que puede ser almacenado en una variable, pasado como argumento o devuelto desde otra función.

Su principal ventaja radica en la localidad. Al definir la lógica de una función justo donde se va a utilizar, el código se vuelve mucho más fácil de entender. Ya no es necesario saltar a otra parte del archivo para ver la definición de una función auxiliar que solo se usa una vez. Esto es especialmente útil al trabajar con los algoritmos de la Librería Estándar de Plantillas (STL), como std::sort, std::for_each o std::find_if.

La Sintaxis Desglosada

A primera vista, la sintaxis de una lambda puede parecer extraña, pero cada parte tiene un propósito claro. La estructura general es la siguiente:

[cláusula_de_captura](parámetros) mutable especificadores excepcion -> tipo_de_retorno { cuerpo_de_la_función };

Analicemos cada componente:

  • Cláusula de Captura []: Esta es la parte más distintiva y poderosa de una lambda. Permite a la función acceder a variables del ámbito circundante (el scope donde la lambda es definida). Veremos las diferentes formas de captura más adelante.
  • Parámetros (): Funciona exactamente igual que la lista de parámetros de una función normal. Si la lambda no recibe parámetros, los paréntesis pueden omitirse (a menos que se usen otros especificadores).
  • Especificadores (opcional): Aquí se pueden incluir palabras clave como mutable (para modificar variables capturadas por valor) o constexpr (desde C++17).
  • Tipo de Retorno -> tipo_de_retorno (opcional): El compilador generalmente puede deducir el tipo de retorno a partir de la sentencia return en el cuerpo de la lambda. Sin embargo, en casos más complejos (por ejemplo, con múltiples sentencias return de tipos diferentes que pueden ser convertidos a uno común), es necesario especificarlo explícitamente.
  • Cuerpo de la Función {}: Contiene el código que se ejecutará cuando la lambda sea invocada. Es idéntico al cuerpo de una función regular.

Un Primer Ejemplo Sencillo

Veamos una lambda simple que suma dos números:

#include <iostream> int main() { // Definimos una lambda y la asignamos a una variable 'sumar' auto sumar = [](int a, int b) -> int { return a + b; }; // La llamamos como si fuera una función normal int resultado = sumar(5, 3); std::cout << "El resultado es: " << resultado << std::endl; // Imprime 8 return 0; }

En este ejemplo, la cláusula de captura está vacía [] porque la lambda no necesita acceder a ninguna variable externa. Recibe dos parámetros (int a, int b), especificamos explícitamente que devuelve un int y su cuerpo simplemente los suma.

What is a lambda function in C++?
In modern C++, mostly auto term is used. It is an expression, a definition by the user. It is used like a class name or method name. Learn about lambda function C++, the parts of the expressions, and how this function can help you drastically improve how you write code.

El Poder de la Cláusula de Captura

La verdadera magia de las lambdas reside en su capacidad para "capturar" variables de su entorno. Esto permite que la lambda interactúe con el estado del contexto en el que fue creada. Existen varias formas de capturar variables, cada una con un propósito específico.

Tabla Comparativa de Modos de Captura

CláusulaDescripciónEjemplo de Uso
[]No captura ninguna variable del entorno. Solo puede usar sus parámetros y variables locales.Perfecto para operaciones puras sin estado.
[=]Captura todas las variables utilizadas en la lambda por valor. La lambda trabaja con una copia de las variables.Útil cuando necesitas leer el estado actual pero no modificarlo.
[&]Captura todas las variables utilizadas en la lambda por referencia. La lambda puede modificar las variables originales.Necesario cuando la lambda debe alterar el estado del entorno.
[x, &y]Captura la variable x por valor y la variable y por referencia.Control granular sobre qué se copia y qué se referencia.
[=, &y]Captura todas las variables por valor, excepto y, que se captura por referencia.Cuando la mayoría de las variables son de solo lectura, pero una necesita ser modificada.
[&, x]Captura todas las variables por referencia, excepto x, que se captura por valor.Para situaciones donde casi todo debe ser modificable, pero se necesita una copia de una variable específica.
[this]Captura el puntero this del objeto actual por referencia, permitiendo el acceso a miembros de la clase.Esencial para usar lambdas dentro de métodos de una clase.
[*this](Desde C++17) Captura una copia del objeto actual (*this) por valor.Importante en programación asíncrona para evitar problemas de tiempo de vida del objeto.

Ejemplo Práctico de Captura

Imaginemos que queremos contar cuántos números en un vector son mayores que un valor límite.

#include <iostream> #include <vector> #include <algorithm> int main() { std::vector<int> numeros = {10, 5, 25, 18, 30, 2}; int limite = 15; int contador = 0; // La lambda captura 'limite' por valor y 'contador' por referencia. std::for_each(numeros.begin(), numeros.end(), [limite, &contador](int n) { if (n > limite) { contador++; } } ); std::cout << "Hay " << contador << " números mayores que " << limite << std::endl; // Imprime: Hay 3 números mayores que 15 return 0; }

Aquí, limite se captura por valor porque solo necesitamos leerlo. contador, en cambio, se captura por referencia porque necesitamos modificar su valor original desde dentro de la lambda.

Aplicaciones Comunes y Casos de Uso

Las lambdas brillan cuando se combinan con otras características de C++, especialmente con la STL.

Ordenando con Criterios Personalizados

Una de las tareas más comunes es ordenar una colección de objetos. Con std::sort y una lambda, es increíblemente sencillo.

What is lambda expression in C++ 11?
A lambda expression in C++11 allows the user to define an anonymous function (a function without any name) inline, which captures variables from the surrounding scope. This makes them a powerful feature for various use cases, like callbacks, sorting, functional programming, etc. Here is the syntax of lambda expression in C++: function_body Where,
#include <iostream> #include <vector> #include <string> #include <algorithm> struct Jugador { std::string nombre; int puntuacion; }; int main() { std::vector<Jugador> jugadores = {{"Ana", 95}, {"Luis", 120}, {"Eva", 80}}; // Ordenar jugadores por puntuación de mayor a menor std::sort(jugadores.begin(), jugadores.end(), [](const Jugador& a, const Jugador& b) { return a.puntuacion > b.puntuacion; } ); std::cout << "Ranking de jugadores:" << std::endl; for (const auto& j: jugadores) { std::cout << j.nombre << ": " << j.puntuacion << std::endl; } return 0; }

Lambdas Genéricas con `auto`

Desde C++14, podemos usar la palabra clave auto en los parámetros de una lambda, creando así una lambda genérica que puede operar con diferentes tipos de datos.

// Una lambda genérica que puede sumar cualquier tipo que soporte el operador + auto sumar_generico = [](auto a, auto b) { return a + b; }; int resultado_int = sumar_generico(5, 10); // 15 double resultado_double = sumar_generico(3.5, 2.2); // 5.7 std::string hola_mundo = sumar_generico(std::string("Hola, "), std::string("Mundo")); // "Hola, Mundo"

Preguntas Frecuentes (FAQ)

¿Una lambda es lo mismo que un puntero a función?

No exactamente. Una lambda sin cláusula de captura puede ser convertida implícitamente a un puntero a función. Sin embargo, una lambda que captura variables es un objeto de función (functor) más complejo. Almacena internamente las variables capturadas, algo que un puntero a función no puede hacer. La forma más flexible de almacenar cualquier tipo de lambda es usando std::function o simplemente auto.

¿Cuándo debería usar una función normal en lugar de una lambda?

Las lambdas son ideales para lógica corta y localizada. Si una pieza de lógica es compleja (más de unas pocas líneas), se reutiliza en múltiples lugares del código, o su propósito es fundamental para la API de una clase, es mejor definirla como una función normal o un método de clase. Esto mejora la legibilidad, la capacidad de mantenimiento y la posibilidad de realizar pruebas unitarias sobre esa lógica.

¿Para qué sirve la palabra clave `mutable` en una lambda?

Por defecto, las variables capturadas por valor (con [=] o [x]) son const dentro del cuerpo de la lambda; no puedes modificarlas. Si necesitas modificar la copia local de una variable capturada por valor, debes marcar la lambda como mutable. Por ejemplo: [contador_copia = 0]() mutable { contador_copia++; }. Ten en cuenta que esto solo afecta a la copia dentro de la lambda, no a la variable original.

Si quieres conocer otros artículos parecidos a Dominando las Expresiones Lambda en C++ puedes visitar la categoría Juegos.

Subir