Skip to content
Theory · Functions · lesson 11 of 13

Lambdas: a first look

A function cannot be defined inside another function: the compiler rejects int twice(int x) { return x * 2; } inside main. Yet a short function is often needed in one place only, and moving it out of main is inconvenient. Such a function can be written as a lambda right where it is needed. It is like a sticky note on the spot where the work happens, instead of a separate manual in a cabinet:

C++
#include <iostream>

int main() {
    auto twice = [](int x) { return x * 2; };
    std::cout << twice(21) << '\n';  // 42
}

Anatomy of a lambda

A lambda is a function without a name, written right inside an expression. A lambda has three parts:

  • the square brackets [] start the lambda;
  • the parentheses hold the parameters;
  • the braces hold the body.

The compiler deduces the return type from return, the way an auto variable gets its type from its value. In the example, x * 2 has type int, so the lambda returns int too.

A lambda is stored in a variable declared with auto: the lambda has a type, but the code cannot name it. Every lambda gets its own type, made up by the compiler. The lambda is called through the variable's name, like an ordinary function: twice(21). A lambda's parameters, like a function's, are passed by value or by reference.

Capturing variables

For the lambda to use the local variable step from main, the variable must be captured. The capture lists the variables the lambda takes from the surrounding code; it is written in the square brackets. A variable can be captured in two ways:

  • [step] copies step when the lambda is created, like a photo of a whiteboard: whatever was written then stays in the picture;
  • [&step] gives the lambda a reference to step itself, like a window through which the board is always visible:
C++
#include <iostream>

int main() {
    int step = 10;
    auto add_copy = [step](int x) { return x + step; };
    auto add_ref = [&step](int x) { return x + step; };
    step = 100;
    std::cout << add_copy(1) << ' ' << add_ref(1) << '\n';  // 11 101
}

add_copy keeps a copy holding 10. It was made when the line with the lambda ran, so the change to step does not reach it. add_ref reads step itself, which is 100 by the time of the call.

Capturing by reference also lets a lambda change the variable: [&count]() { ++count; } increases count from main on every call. A copy from [step] can only be read: [step]() { ++step; } does not compile. This is the same choice a function's parameters make: a copy, or another name for the same variable. Several variables are listed with commas: [a, &b].

Common mistake

A variable is used in a lambda but not captured:

C++
int main() {
    int step = 10;
    auto add = [](int x) { return x + step; };  // ✗
    return add(1);
}
TEXT
error: variable 'step' cannot be implicitly captured in a lambda with no capture-default specified

This is a compiler error: empty brackets [] mean the lambda captures nothing. Add the variable to the capture: [step] or [&step].

Key points

  • A lambda is a function without a name: [] starts it, () holds the parameters, and {} holds the body.
  • A lambda is kept in an auto variable and called through its name, like an ordinary function.
  • [step] copies the variable when the lambda is created, while [&step] reads the variable itself on every call.
  • A variable the lambda uses but did not capture is a compiler error.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way