Site

Lambdas — Anonymous Functions

Tutorial S7.0  •  C++ / Learn / StdLib

S7.0 What This Teaches

Lambdas are anonymous function objects defined inline. This tutorial covers:

S7.1 Lambda Syntax

// [capture](params) -> return_type { body }
// return type is usually inferred

auto add = [](int a, int b) { return a + b; };
auto greet = [](const std::string& name) -> std::string {
    return "Hello, " + name + "!";
};

std::cout << add(3, 4) << "\n";         // 7
std::cout << greet("Alice") << "\n";    // Hello, Alice!

S7.2 Capture Modes

int x = 10, y = 20;

auto by_value = [x, y]()    { return x + y; };    // copies of x and y
auto by_ref   = [&x, &y]()  { x++; return x + y; }; // references to x and y
auto all_val  = [=]()       { return x + y; };    // capture all by value
auto all_ref  = [&]()       { x++; y++; };        // capture all by reference

// mutable: modify the captured copy (not the original)
auto inc_copy = [x]() mutable { return ++x; };   // x in outer scope unchanged
Prefer explicit captures ([x, &y]) over default captures ([=], [&]). Explicit captures make it clear what the lambda depends on.

S7.3 Lambdas with Algorithms

std::vector<int> v = {3, 1, 4, 1, 5, 9};

// sort descending
std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; });

// find first even
auto it = std::find_if(v.begin(), v.end(), [](int x){ return x % 2 == 0; });

// filter to new vector
std::vector<int> big;
std::copy_if(v.begin(), v.end(), std::back_inserter(big),
             [](int x){ return x > 3; });

S7.4 std::function

#include <functional>

std::function<int(int, int)> op;

op = [](int a, int b) { return a + b; };
std::cout << op(3, 4) << "\n";   // 7

op = [](int a, int b) { return a * b; };
std::cout << op(3, 4) << "\n";   // 12
std::function can store any callable with a matching signature. It has overhead from type erasure - use auto for local lambdas and std::function only when you need to store or pass callables polymorphically.

S7.5 Returning Lambdas

auto make_adder(int n) {
    return [n](int x) { return x + n; };
}

auto add5  = make_adder(5);
auto add10 = make_adder(10);

std::cout << add5(3)  << "\n";   // 8
std::cout << add10(3) << "\n";   // 13
The returned lambda captures n by value, so each call to make_adder creates a closure with its own copy of n.

S7.6 Example - All Together

// Lambdas - syntax, capture modes, algorithms, std::function, returning lambdas.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
    auto add = [](int a, int b) { return a + b; };
    std::cout << "add(3,4)=" << add(3, 4) << "\n";

    int offset = 10;
    auto add_offset = [offset](int x) { return x + offset; };
    std::cout << "add_offset(5)=" << add_offset(5) << "\n";

    int counter = 0;
    auto inc = [&counter]() { ++counter; };
    inc(); inc(); inc();
    std::cout << "counter=" << counter << "\n";

    std::vector<int> v = {5, 3, 8, 1, 9};
    std::sort(v.begin(), v.end(), [](int a, int b){ return a > b; });
    std::cout << "sorted desc: ";
    for (int x : v) std::cout << x << " ";
    std::cout << "\n";

    auto make_multiplier = [](int f){ return [f](int x){ return x * f; }; };
    auto triple = make_multiplier(3);
    std::cout << "triple(7)=" << triple(7) << "\n";
    return 0;
}
add(3,4)=7
add_offset(5)=15
counter=3
sorted desc: 9 8 5 3 1
triple(7)=21

S7.7 Exercise

Exercise
  • Write a lambda is_prime and use it with copy_if to extract all primes from a vector<int> of 1 through 30.
  • Write a function apply(std::function<int(int)> f, int x) and call it with three different lambdas (square, double, negate).
  • Write a factory function make_between_checker(int lo, int hi) that returns a lambda capturing lo and hi and returning true if its argument is in range.

S7.8 Common Mistakes

Dangling reference capture

std::function<int()> get_lambda() {
    int local = 42;
    return [&local]() { return local; };   // dangling: local is gone after return
}
Capture by value when the lambda outlives the captured variable.

Forgetting mutable

int n = 5;
auto f = [n]() { return ++n; };   // error: n captured by value is const by default
Add mutable to modify a value-captured variable inside the lambda.

std::function overhead in performance-critical code

std::function uses type erasure and may allocate. In tight loops, prefer auto or template parameters for callables.

S7.9 Key Terms

TermMeaning
lambdaInline anonymous function object; syntax [capture](params){ body }
capture clause[] specifies which outer variables the lambda can access
capture by value [x]Lambda gets its own copy of x at the time of creation
capture by reference [&x]Lambda holds a reference to x; changes are visible outside
mutableAllows a value-captured variable to be modified inside the lambda
closureLambda together with its captured environment
std::function<R(Args)>Type-erased callable wrapper; stores any callable with matching signature