Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Modern C++ Features

Lambda Expressions

Lambda Expressions: Anonymous Functions

C++11 introduced lambdas: inline anonymous function objects with concise syntax. Lambdas are extensively used with algorithms, callbacks, and asynchronous operations.

auto sum = [](int a, int b) { return a + b; };
std::cout << sum(3, 4);  // Outputs: 7

std::vector<int> v = {4, 2, 5, 1, 3};
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
// v is now {5, 4, 3, 2, 1}

Lambda Syntax

[captures](parameters) specifiers -> return_type { body }
  • captures: Variables from enclosing scope
  • parameters: Function parameters (optional, default ())
  • specifiers: mutable, constexpr, noexcept
  • return_type: Can be deduced or explicit
  • body: Function implementation

Minimal Lambda

auto f = [] { return 42; };  // No parameters, deduced return type

Capture Modes

Lambdas can capture variables from their enclosing scope:

Capture by Value

int x = 10;
auto lambda = [x]() { return x * 2; };  // Captures x by value
x = 20;
std::cout << lambda();  // Outputs: 20 (captured value at lambda creation)

Capture by Reference

int x = 10;
auto lambda = [&x]() { return x * 2; };  // Captures x by reference
x = 20;
std::cout << lambda();  // Outputs: 40 (current value of x)

Mixed Captures

int x = 1, y = 2, z = 3;
auto lambda = [x, &y, z]() {
    // x and z by value, y by reference
};

Default Captures

int a = 1, b = 2;

auto lambda1 = [=]() { return a + b; };  // Capture all by value
auto lambda2 = [&]() { return a + b; };  // Capture all by reference

auto lambda3 = [=, &b]() { return a + b; };  // All by value except b
auto lambda4 = [&, a]() { return a + b; };   // All by reference except a

Warning: Capturing [=] doesn’t capture this in member functions (C++17 changed this). Prefer explicit captures.

Mutable Lambdas

By default, value-captured variables are const. Use mutable to modify them:

int x = 0;
auto counter = [x]() mutable {
    return ++x;  // Modifies lambda's copy of x
};

std::cout << counter();  // 1
std::cout << counter();  // 2
std::cout << x;          // 0 (original x unchanged)

Generic Lambdas (C++14)

Parameters can use auto for generic types:

auto print = [](const auto& x) {
    std::cout << x << '\\n';
};

print(42);       // Works with int
print(3.14);     // Works with double
print("hello");  // Works with const char*

Equivalent to a template function:

struct Lambda {
    template<typename T>
    void operator()(const T& x) const {
        std::cout << x << '\\n';
    }
};

Init Captures (C++14)

Capture with initialization, enabling move-only types:

auto ptr = std::make_unique<int>(42);

auto lambda = [p = std::move(ptr)]() {
    return *p;
};

// ptr is now nullptr, lambda owns the unique_ptr

Rename and transform during capture:

int x = 5;
auto lambda = [y = x * 2]() {
    return y;  // y is 10
};

Return Type Deduction

Usually automatic, but can be explicit:

auto lambda1 = [](int x) { return x * 2; };  // Deduced: int

auto lambda2 = [](int x) -> double {  // Explicit: double
    return x * 2.5;
};

Required when return type is ambiguous:

auto lambda = [](bool flag) {
    if (flag)
        return 1;      // int
    else
        return 2.5;    // double - ERROR: inconsistent types
};

// Fix with explicit return type:
auto lambda = [](bool flag) -> double {
    if (flag) return 1;
    else return 2.5;
};

Lambda Type and std::function

Each lambda has a unique compiler-generated type. Use auto to store:

auto lambda = [](int x) { return x * 2; };

std::function provides type erasure but has overhead:

#include <functional>

std::function<int(int)> func = [](int x) { return x * 2; };
// More flexible but slower than auto

Prefer auto unless you need type erasure or reassignment.

Immediately Invoked Lambda Expression (IIFE)

Useful for complex initialization:

const auto value = [&]() {
    if (condition1) return compute1();
    else if (condition2) return compute2();
    else return default_value();
}();  // Immediately invoked

Lambdas with Algorithms

Classic use case: predicates and projections

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

// Remove odd numbers
v.erase(std::remove_if(v.begin(), v.end(),
                       [](int x) { return x % 2 != 0; }),
        v.end());

// Count elements > 3
auto count = std::count_if(v.begin(), v.end(),
                           [](int x) { return x > 3; });
Conceptual Check

What does [=] capture in a member function (C++17+)?

Runtime Environment

Interactive Lab

1#include <iostream>
2#include <vector>
3#include <algorithm>
4 
5int main() {
6 std::vector<int> v = {1, 2, 3, 4, 5};
7 int threshold = 3;
8
9 // Generic lambda with capture
10 auto filter = [threshold](const auto& x) {
11 return x > threshold;
12 };
13
14 auto count = std::count_if(v.begin(), v.end(), filter);
15 std::cout << "Elements > " << threshold << ": " << count << '\n';
16
17 // IIFE for initialization
18 const auto sum = [&v]() {
19 int total = 0;
20 for (auto x : v) total += x;
21 return total;
22 }();
23
24 std::cout << "Sum: " << sum << '\n';
25 return 0;
26}
System Console

Waiting for signal...