Skip to content

🎯 Lambda Expressions — Inline Functions

Lambda là anonymous function — viết function ngay tại chỗ cần dùng, không cần đặt tên riêng.

Lambda Syntax

cpp
[capture](parameters) -> return_type { body }
┌─────────────────────────────────────────────────────────────────┐
│                    LAMBDA ANATOMY                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   [=, &x](int a, int b) -> double { return a + b + x; }        │
│    │        │               │              │                   │
│    │        │               │              └── Body            │
│    │        │               └── Return type (optional)         │
│    │        └── Parameters                                     │
│    └── Capture list                                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Basic Lambda Examples

Simplest Lambda

cpp
#include <iostream>

int main() {
    // Lambda không capture, không params
    auto greet = []() {
        std::cout << "Hello, Lambda!" << std::endl;
    };
    
    greet();  // Hello, Lambda!
    
    // Lambda với params
    auto add = [](int a, int b) {
        return a + b;
    };
    
    std::cout << add(3, 4) << std::endl;  // 7
    
    // Lambda inline
    std::cout << [](int x) { return x * x; }(5) << std::endl;  // 25
    
    return 0;
}

Lambda với Return Type

cpp
// Automatic return type deduction
auto multiply = [](int a, int b) {
    return a * b;  // Returns int
};

// Explicit return type (cần thiết khi có nhiều return paths khác type)
auto divide = [](int a, int b) -> double {
    if (b == 0) return 0.0;
    return static_cast<double>(a) / b;
};

std::cout << divide(5, 2) << std::endl;  // 2.5

Capture List — Biến bên ngoài

Capture by Value [=]

cpp
int x = 10;
int y = 20;

// Capture ALL by value
auto f1 = [=]() {
    std::cout << x + y << std::endl;  // 30
    // x = 50;  // ❌ Error: captured by value = const!
};

// Capture specific by value
auto f2 = [x]() {
    std::cout << x << std::endl;  // 10
};

x = 100;  // Change original
f1();     // Still prints 30 (captured value)

Capture by Reference [&]

cpp
int counter = 0;

// Capture ALL by reference
auto increment = [&]() {
    ++counter;  // ✅ Modifies original
};

increment();
increment();
std::cout << counter << std::endl;  // 2

// Capture specific by reference
auto f = [&counter]() {
    counter *= 2;
};

Mixed Capture

cpp
int x = 1, y = 2, z = 3;

// Default by value, z by reference
auto f1 = [=, &z]() {
    // x, y captured by value (read-only)
    // z captured by reference (modifiable)
    z = x + y;
};

// Default by reference, x by value
auto f2 = [&, x]() {
    // y, z captured by reference
    // x captured by value
    y = x * 10;
};

Capture Summary

SyntaxMeaning
[]Không capture gì
[x]x by value
[&x]x by reference
[=]All by value
[&]All by reference
[=, &x]All by value, x by ref
[&, x]All by ref, x by value
[this]this pointer (trong class)
[*this]Copy of *this (C++17)

mutable Lambdas

Mặc định, biến capture by value là const. Dùng mutable để modify:

cpp
int x = 10;

// ❌ Error without mutable
// auto f = [x]() { x++; return x; };  

// ✅ OK with mutable
auto f = [x]() mutable {
    x++;  // Modifies local copy
    return x;
};

std::cout << f() << std::endl;  // 11
std::cout << f() << std::endl;  // 12
std::cout << x << std::endl;    // 10 (original unchanged)

Generic Lambdas (C++14)

cpp
// C++14: auto parameters
auto print = [](auto x) {
    std::cout << x << std::endl;
};

print(42);        // int
print(3.14);      // double
print("Hello");   // const char*

// Generic with multiple params
auto add = [](auto a, auto b) {
    return a + b;
};

std::cout << add(1, 2) << std::endl;      // 3 (int)
std::cout << add(1.5, 2.5) << std::endl;  // 4.0 (double)
std::cout << add(std::string("Hello "), std::string("World")) << std::endl;

Init Capture (C++14)

cpp
// Move-only types
auto ptr = std::make_unique<int>(42);

// ❌ Cannot capture unique_ptr by value (non-copyable)
// auto f = [ptr]() { };

// ✅ Move into lambda
auto f = [p = std::move(ptr)]() {
    std::cout << *p << std::endl;
};

f();  // 42

// Create new variable in capture
auto g = [y = 10 * 2]() {
    return y;  // 20
};

Lambda với STL Algorithms

std::sort với Lambda

cpp
#include <algorithm>
#include <vector>

std::vector<int> v = {5, 2, 8, 1, 9};

// Ascending (default)
std::sort(v.begin(), v.end());

// Descending với lambda
std::sort(v.begin(), v.end(), [](int a, int b) {
    return a > b;  // Greater = earlier
});
// [9, 8, 5, 2, 1]

std::find_if

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

// Tìm số chẵn đầu tiên
auto it = std::find_if(v.begin(), v.end(), [](int x) {
    return x % 2 == 0;
});

if (it != v.end()) {
    std::cout << "First even: " << *it << std::endl;  // 2
}

std::transform

cpp
std::vector<int> v = {1, 2, 3, 4, 5};
std::vector<int> squared(v.size());

std::transform(v.begin(), v.end(), squared.begin(), 
               [](int x) { return x * x; });
// squared = [1, 4, 9, 16, 25]

std::for_each

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

std::for_each(v.begin(), v.end(), [](int& x) {
    x *= 2;
});
// v = [2, 4, 6, 8, 10]

std::count_if

cpp
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

int evenCount = std::count_if(v.begin(), v.end(), [](int x) {
    return x % 2 == 0;
});
// evenCount = 5

Lambda vs Function Pointer

cpp
// Function pointer
bool greaterThan5(int x) { return x > 5; }

void demo() {
    std::vector<int> v = {3, 7, 2, 8, 1, 9};
    
    // Function pointer
    auto c1 = std::count_if(v.begin(), v.end(), greaterThan5);
    
    // Lambda — inline và có thể capture!
    int threshold = 5;
    auto c2 = std::count_if(v.begin(), v.end(), [threshold](int x) {
        return x > threshold;
    });
    
    // Lambda có thể capture, function pointer thì không!
}

Immediately Invoked Lambda (IIFE)

cpp
// Gọi ngay lập tức
const auto result = []() {
    // Complex initialization
    std::vector<int> temp;
    for (int i = 0; i < 10; ++i) {
        temp.push_back(i * i);
    }
    return temp;
}();  // () gọi ngay!

// Useful for const initialization
const auto config = []() {
    Config c;
    c.loadFromFile("settings.json");
    c.validate();
    return c;
}();

Lambda trong Class

cpp
class Button {
    std::string label_;
    std::function<void()> onClick_;
    
public:
    Button(const std::string& label) : label_(label) {}
    
    void setOnClick(std::function<void()> callback) {
        onClick_ = callback;
    }
    
    void click() {
        if (onClick_) onClick_();
    }
};

int main() {
    Button btn("Submit");
    
    int counter = 0;
    btn.setOnClick([&counter]() {
        counter++;
        std::cout << "Clicked! Count: " << counter << std::endl;
    });
    
    btn.click();  // Clicked! Count: 1
    btn.click();  // Clicked! Count: 2
}

C++20 Improvements

cpp
// Template lambda
auto add = []<typename T>(T a, T b) {
    return a + b;
};

// Lambda with capture default in unevaluated context
// (advanced - for concept constraints)

📚 Tổng kết

FeatureSyntax
Basic lambda[](int x) { return x * 2; }
Capture by value[x] or [=]
Capture by ref[&x] or [&]
Mixed[=, &x] or [&, x]
Mutable[x]() mutable { x++; }
Generic (C++14)[](auto x) { }
Init capture (C++14)[y = expr] or [p = std::move(ptr)]
IIFE[]() { }();

➡️ Tiếp theo

Tiếp theo: Sorting & Comparators — Custom sort rules.