Menu
Coddy logo textTech

Expresiones Lambda en profundidad

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 85 de 104.

Las expresiones lambda, introducidas en C++11, son funciones anónimas que puedes definir en línea. Aunque has visto lambdas básicas con algoritmos de la STL, comprender su sintaxis completa desbloquea capacidades potentes para capturar variables y controlar cómo se accede a ellas.

La sintaxis completa de una lambda es: [capture](parameters) mutable -> return_type { body }. La cláusula de captura determina a qué variables externas puede acceder la lambda y cómo:

#include <iostream>

int main() {
    int x = 10;
    int y = 20;
    
    auto byValue = [x]() { return x * 2; };        // Copia de x
    auto byRef = [&y]() { y += 5; };               // Referencia a y
    auto allByValue = [=]() { return x + y; };     // Copiar todo
    auto allByRef = [&]() { x++; y++; };           // Referenciar todo
    auto mixed = [x, &y]() { y += x; };            // Mezclar ambos
    
    byRef();
    std::cout << y << "\n";  // 25
}

Por defecto, las variables capturadas por valor son const dentro de la lambda. La palabra clave mutable permite la modificación de estas copias:

int counter = 0;
auto increment = [counter]() mutable {
    return ++counter;  // Modifica la copia de la lambda
};

std::cout << increment() << "\n";  // 1
std::cout << increment() << "\n";  // 2
std::cout << counter << "\n";      // 0 - el original no cambia

C++14 añadió init captures (capturas de inicialización), permitiéndote crear nuevas variables o mover objetos a la lambda:

auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
    return *p;
};  // Propiedad transferida a la lambda

Las lambdas son particularmente útiles en OOP cuando necesitas pasar comportamiento como parámetro —para callbacks, comparadores personalizados o controladores de eventos— sin definir objetos de función independientes.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de manejo de eventos que muestre el poder de las expresiones lambda con diferentes modos de captura. Crearás un despachador de eventos simple que almacena e invoca devoluciones de llamada (callbacks), demostrando cómo las lambdas pueden capturar el estado externo de varias maneras.

Organizarás tu código en tres archivos:

  • EventDispatcher.h: Define una clase EventDispatcher que gestiona las devoluciones de llamada de eventos.

    Tu despachador debe almacenar las devoluciones de llamada utilizando un std::vector de std::function<void()>. Incluye estos métodos:

    • addCallback(std::function<void()> callback) — añade una devolución de llamada a la lista
    • fireAll() — invoca todas las devoluciones de llamada almacenadas en orden
    • clear() — elimina todas las devoluciones de llamada

    Necesitarás incluir <functional> y <vector>.

  • EventDispatcher.cpp: Implementa los métodos para tu despachador. El método fireAll() simplemente debe iterar a través de todas las devoluciones de llamada e invocar cada una.
  • main.cpp: Lee dos entradas:
    1. Un número base (entero)
    2. Un multiplicador (entero)

    Crea un EventDispatcher y demuestra diferentes técnicas de captura de lambda añadiendo tres devoluciones de llamada:

    1. Una lambda que capture el número base por valor e imprima: Base value: [base]
    2. Una lambda que capture el multiplicador por referencia, lo incremente en 1, y luego imprima: Multiplier after increment: [multiplier]
    3. Una lambda mutable que capture una variable contador (inicializada en 0) por valor, la incremente cada vez que sea llamada, e imprima: Call count: [counter]

    Después de añadir todas las devoluciones de llamada, llama a fireAll() dos veces para ver cómo se comportan los diferentes modos de captura a través de múltiples invocaciones. Entre las dos llamadas a fireAll(), imprime --- como separador.

    Finalmente, después de ambas rondas, imprime el valor final de la variable multiplicador desde main para mostrar cómo la captura por referencia la afectó: Final multiplier: [multiplier]

Por ejemplo, con las entradas 10 y 5:

Base value: 10
Multiplier after increment: 6
Call count: 1
---
Base value: 10
Multiplier after increment: 7
Call count: 1
Final multiplier: 7

Con las entradas 42 y 0:

Base value: 42
Multiplier after increment: 1
Call count: 1
---
Base value: 42
Multiplier after increment: 2
Call count: 1
Final multiplier: 2

Observa los comportamientos clave: la captura por valor mantiene la base original sin cambios, la captura por referencia modifica la variable multiplicador real en main (acumulándose entre llamadas), y el contador de la lambda mutable lee 1 en cada ronda. Esto se debe a que fireAll() itera sus devoluciones de llamada por valor: cada ronda invoca una copia fresca de la std::function almacenada, cuyo contador capturado comienza en 0 y se incrementa a 1, por lo que el estado propio de la devolución de llamada almacenada nunca avanza.

Pruébalo tú mismo

#include <iostream>
#include "EventDispatcher.h"

using namespace std;

int main() {
    int base;
    int multiplier;
    cin >> base;
    cin >> multiplier;
    
    EventDispatcher dispatcher;
    
    // TODO: Agregar una lambda que capture base POR VALOR
    // Debería imprimir: "Base value: [base]"
    
    // TODO: Agregar una lambda que capture multiplier POR REFERENCIA
    // Debería incrementar multiplier en 1, luego imprimir: "Multiplier after increment: [multiplier]"
    
    // TODO: Agregar una lambda MUTABLE que capture un contador (inicializado en 0) por valor
    // Debería incrementar el contador e imprimir: "Call count: [counter]"
    
    // TODO: Llamar a fireAll() para invocar todos los callbacks
    
    // TODO: Imprimir "---" como separador
    
    // TODO: Llamar a fireAll() nuevamente
    
    // TODO: Imprimir el valor final de multiplier: "Final multiplier: [multiplier]"
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos