LearnFrenzy
Sign in Take a free test

Control Flow, Functions and Lambdas · Lesson 9 of 33

Lambda Expressions in C++

6 min read Updated Nov 7, 2026 Saurabh Samir

A lambda is a small, unnamed function you write right where you need it – most often to tell an STL algorithm how to sort, filter or count. Lambdas are everywhere in modern C++, and they're essential in competitive programming (custom sort orders) and interviews.

In this lesson

  • Lambda syntax
  • Captures: by value, by reference, mixed
  • Lambdas with STL algorithms
  • Generic lambdas, mutable lambdas and std::function

The syntax

#include <iostream>

int main() {
    auto square = [](int x) { return x * x; };                  // [captures](params) { body }
    auto add = [](int a, int b) -> long long { return 1LL * a + b; };   // explicit return type
    auto hello = [] { std::cout << "Hello from a lambda\n"; };  // no parameters

    std::cout << square(9) << ' ' << add(2'000'000'000, 2'000'000'000) << '\n';
    hello();
    std::cout << [](int n) { return n % 2 == 0; }(10) << '\n';     // defined and called at once
    return 0;
}

Output

81 4000000000
Hello from a lambda
1

Anatomy of a lambda

  1. [ ]capture list: which outside variables the lambda can use
  2. ( )parameters, like a normal function
  3. -> typeoptional return type (usually deduced)
  4. { }the body

Captures

#include <iostream>

int main() {
    int threshold = 50;
    int counter = 0;

    auto above = [threshold](int x) { return x > threshold; };   // copy of threshold
    auto count_call = [&counter] { counter++; };                  // reference to counter

    threshold = 90;                    // the lambda kept its own copy (50)
    std::cout << above(70) << '\n';
    count_call(); count_call();
    std::cout << "counter = " << counter << '\n';

    int a = 1, b = 2;
    auto all_by_value = [=] { return a + b; };       // capture everything used, by value
    auto all_by_ref = [&] { a *= 10; b *= 10; };     // capture everything used, by reference
    all_by_ref();
    std::cout << all_by_value() << ' ' << a + b << '\n';   // 3 (copies taken earlier), 30
    return 0;
}

Output

1
counter = 2
3 30
CaptureMeaning
[]nothing
[x] / [&x]x by value (copy) / by reference
[=] / [&]everything used, by value / by reference
[=, &total]everything by value except total by reference
[this]the current object, inside member functions

Dangling captures

A lambda that captures a local variable by reference and outlives it – for example one returned from a function – refers to a destroyed variable. Capture by value whenever the lambda may be stored or run later.

Lambdas with STL algorithms

#include <algorithm>
#include <iostream>
#include <numeric>
#include <string>
#include <vector>

struct Student { std::string name; int marks; };

int main() {
    std::vector<int> v{5, 12, 7, 20, 3, 18};
    std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });     // descending
    for (int x : v) std::cout << x << ' ';
    std::cout << '\n';

    int limit = 10;
    auto big = std::count_if(v.begin(), v.end(), [limit](int x) { return x > limit; });
    auto first_odd = std::find_if(v.begin(), v.end(), [](int x) { return x % 2 != 0; });
    std::cout << big << " above " << limit << ", first odd: " << *first_odd << '\n';

    std::vector<Student> cls{{"Asha", 88}, {"Ravi", 74}, {"Meera", 88}, {"Kiran", 91}};
    std::sort(cls.begin(), cls.end(), [](const Student& a, const Student& b) {
        if (a.marks != b.marks) return a.marks > b.marks;   // higher marks first
        return a.name < b.name;                             // then by name
    });
    for (const auto& s : cls) std::cout << s.name << ' ' << s.marks << '\n';

    int total = std::accumulate(cls.begin(), cls.end(), 0,
                                [](int sum, const Student& s) { return sum + s.marks; });
    std::cout << "total " << total << '\n';
    return 0;
}

Output

20 18 12 7 5 3 
3 above 10, first odd: 7
Kiran 91
Asha 88
Meera 88
Ravi 74
total 341

Comparator rule

A sort comparator must return true only when the first argument should come strictly before the second. Using >= or <= breaks that rule ("strict weak ordering") and can crash std::sort – a classic contest bug.

Generic lambdas, mutable and std::function

#include <functional>
#include <iostream>
#include <map>
#include <string>

int main() {
    auto twice = [](auto x) { return x + x; };                // generic: works for any type with +
    std::cout << twice(21) << ' ' << twice(1.5) << ' ' << twice(std::string{"ab"}) << '\n';

    auto next_id = [id = 1000]() mutable { return ++id; };   // init-capture + mutable: keeps state
    std::cout << next_id() << ' ' << next_id() << '\n';

    std::map<char, std::function<int(int, int)>> ops{       // store different lambdas by type
        {'+', [](int a, int b) { return a + b; }},
        {'-', [](int a, int b) { return a - b; }},
        {'*', [](int a, int b) { return a * b; }},
    };
    for (const auto& [symbol, op] : ops) std::cout << "12 " << symbol << " 4 = " << op(12, 4) << '\n';
    return 0;
}

Output

42 3 abab
1001 1002
12 * 4 = 48
12 + 4 = 16
12 - 4 = 8

Every lambda has its own unique type, so auto is the usual way to store one. std::function<R(Args...)> can hold any callable with that signature – useful in containers and callbacks, at a small runtime cost.

Practice

Try it yourself

  1. Sort words by length, and alphabetically when lengths are equal.

    Show solution
    #include <algorithm>
    #include <iostream>
    #include <string>
    #include <vector>
    
    int main() {
        std::vector<std::string> w{"stack", "map", "set", "vector", "queue", "deque"};
        std::sort(w.begin(), w.end(), [](const std::string& a, const std::string& b) {
            return a.size() != b.size() ? a.size() < b.size() : a < b;
        });
        for (const auto& s : w) std::cout << s << ' ';
        std::cout << '\n';
        return 0;
    }

    Output

    map set deque queue stack vector 

Quick check

What does [&] capture?

Every outside variable the lambda uses, by reference.

Why does a by-value capture not see later changes?

It copied the value when the lambda was created.

What must a sort comparator return for equal elements?

false – it must be a strict "comes before" test.

Summary

  • Lambdas are inline functions: [captures](params) { body }.
  • Capture by value for safety, by reference to modify or avoid copies; watch dangling references.
  • Pass lambdas to sort, count_if, find_if, accumulate; comparators must be strict.

Finished this lesson?

Mark it complete to track your progress, then carry on with the next lesson.

Next lesson

Search LearnFrenzy

Esc

Quick links