DMarlon Studio

Lesson 11 · 11 of 20

Lambdas and captures · C++

Write functions on the spot with C++ lambdas: sort tasks, capture variables by value or by reference, and use mutable and auto parameters.

C++ · Lambdas and captures in DMarlon Studio: the example's code and its output in the terminal

A lambda is an unnamed function written right where it is needed: [captures](parameters) { body }. byPriority compares two tasks by priority and is passed to std::stable_sort, which orders them from highest to lowest and keeps the original order for equal priorities ("Fix login" before "Review PR").

The square brackets hold the captures, the outside variables the lambda may use. [budget, &planned] captures budget by value (a copy, which cannot be changed inside) and planned by reference, with &: when fits adds hours, it changes the variable in main. That is how tasks are planned until the 4 hours are full, and "Write docs" is left for later.

A lambda that captures by value cannot modify its copy unless it is marked `mutable`. nextTicket keeps its own counter, starting at 100 and giving 101 and 102, while main's ticket stays at 100. There are also [=] (everything by value) and [&] (everything by reference), although naming each variable is clearer.

With auto parameters you get a generic lambda, which behaves like a template: twice(21) gives 42 and twice(std::string("ha")) gives "haha". Every lambda has its own type that only the compiler knows, which is why it is stored in an auto variable.

Code · main.cpp

#include <algorithm>#include <iostream>#include <string>#include <vector>struct Task { std::string title; int priority; double hours; };int main() {    std::vector<Task> tasks = {{"Fix login", 3, 2.5}, {"Write docs", 1, 4}, {"Release", 5, 1}, {"Review PR", 3, 0.5}};    // A lambda is a function written in place: [captures](parameters) { body }.    auto byPriority = [](const Task& a, const Task& b) { return a.priority > b.priority; };    std::stable_sort(tasks.begin(), tasks.end(), byPriority);    // [budget] copies the variable; [&planned] works on the original.    double budget = 4.0, planned = 0;    auto fits = [budget, &planned](const Task& t) {        if (planned + t.hours > budget) return false;        planned += t.hours;        return true;    };    for (const Task& t : tasks)        std::cout << (fits(t) ? "[today] " : "[later] ") << t.title << " (p" << t.priority << ")\n";    std::cout << "Planned: " << planned << " of " << budget << " hours\n";    // mutable lets a lambda change its own copy of a captured value.    int ticket = 100;    auto nextTicket = [ticket]() mutable { return ++ticket; };    int a = nextTicket(), b = nextTicket();    std::cout << "Tickets " << a << " and " << b << ", but ticket is still " << ticket << "\n";    auto twice = [](auto x) { return x + x; };     // generic lambda: auto parameter    std::cout << twice(21) << " " << twice(std::string("ha")) << "\n";}

Output · c++ -std=c++17 main.cpp -o main && ./main

[today] Release (p5)
[today] Fix login (p3)
[today] Review PR (p3)
[later] Write docs (p1)
Planned: 4 of 4 hours
Tickets 101 and 102, but ticket is still 100
42 haha

Key points

  • A lambda is [captures](parameters) { body }.
  • [x] copies the variable; [&x] works on the original.
  • mutable lets a lambda change its by-value copy.
  • With auto parameters, the lambda is generic.
  • Lambdas are ideal for passing criteria to sort and other algorithms.

Exercise

Set the budget to 3 hours and add a byHours lambda that sorts from fewest to most hours. Plan with that order and compare how many tasks fit now.