Lambda expression (sejak C++11) adalah cara membuat fungsi anonim langsung di tempat pemakaiannya. Karena bisa menangkap (capture) variabel dari scope sekitar, lambda sangat memudahkan penulisan callback, comparator, dan operasi std::for_each/std::transform/std::sort tanpa harus mendefinisikan functor terpisah. Pada Chapter ini kita akan membedah sintaks [capture](params) -> ret { body }, berbagai mode capture, mutable lambda, generic lambda (C++14), serta penyimpanan lambda di std::function.

Sintaks Lambda #

text
[ capture ] ( params ) mutable -> return_type { body }

Komponen:

  • [capture]: daftar variabel dari scope luar yang ditangkap. Wajib ada (boleh kosong []).
  • (params): parameter seperti fungsi biasa. Boleh dihilangkan bila tidak ada.
  • mutable (opsional): mengizinkan modifikasi capture by-value.
  • -> ret (opsional): trailing return type. Bila tidak ditulis, tipe balikan dideduksi dari return.
  • { body }: tubuh lambda.

Mode Capture #

SintaksArti
[]Tidak ada capture
[=]Semua variabel luar by-value
[&]Semua variabel luar by-reference
[x]Hanya x by-value
[&x]Hanya x by-reference
[=, &x]Semua by-value, kecuali x by-reference
[&, x]Semua by-reference, kecuali x by-value
[this]Capture pointer this (untuk lambda di dalam method kelas)

Mekanisme: Lambda sebagai Closure Type #

Setiap lambda adalah tipe unik yang di-generate compiler — sebuah closure type dengan operator() yang sesuai. Lambda yang tidak capture dapat dikonversi implisit ke function pointer; lambda yang capture tidak bisa. Saat lambda ditangkap oleh std::function, ia disimpan di heap bila ukuran closure melebihi small-buffer optimization.

Capture by-value membuat copy variabel saat lambda dibuat. Capture by-reference menyimpan referensi — modifikasi variabel luar terlihat dari lambda, dan bila variabel luar sudah hancur saat lambda dipanggil, terjadi dangling reference.

Kode: Sepuluh Demo Lambda #

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

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

    // 1) Lambda tanpa capture
    auto is_genap = [](int x) { return x % 2 == 0; };
    std::cout << "is_genap(4) = " << is_genap(4) << '\n';

    // 2) Lambda dengan std::sort
    std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });
    std::cout << "desc: ";
    for (int x : v) std::cout << x << ' ';
    std::cout << '\n';

    // 3) Capture by value [=]
    int threshold = 5;
    auto above = [threshold](int x) { return x > threshold; };
    int n = std::count_if(v.begin(), v.end(), above);
    std::cout << "jumlah di atas " << threshold << " = " << n << '\n';

    // 4) Capture by reference [&]
    int sum = 0;
    std::for_each(v.begin(), v.end(), [&sum](int x) { sum += x; });
    std::cout << "sum via capture [&] = " << sum << '\n';

    // 5) Capture mixed
    int factor = 3;
    long long acc = 0;
    std::for_each(v.begin(), v.end(),
                  [factor, &acc](int x) { acc += x * factor; });
    std::cout << "sum(x * " << factor << ") = " << acc << '\n';

    // 6) Default capture [=] atau [&]
    int base = 100;
    auto add_base = [=](int x) { return x + base; };
    std::cout << "add_base(7) = " << add_base(7) << '\n';

    // 7) Mutable lambda — modifikasi capture by-value
    int counter = 0;
    auto next = [counter]() mutable { return ++counter; };
    std::cout << "next(): " << next() << ' ' << next() << ' ' << next() << '\n';
    std::cout << "counter (outer) = " << counter << '\n';  // tetap 0

    // 8) Generic lambda (C++14) — auto parameter
    auto maxx = [](auto a, auto b) { return a > b ? a : b; };
    std::cout << "max(3, 7)   = " << maxx(3, 7) << '\n';
    std::cout << "max(2.5, 1.5) = " << maxx(2.5, 1.5) << '\n';
    std::cout << "max(\"abc\", \"abd\") = " << maxx("abc", "abd") << '\n';

    // 9) Lambda dengan trailing return type
    auto half = [](int x) -> double { return x / 2.0; };
    std::cout << "half(5) = " << half(5) << '\n';

    // 10) Simpan lambda di std::function
    std::function<int(int,int)> op = [](int a, int b) { return a + b; };
    std::cout << "op(3,4) = " << op(3, 4) << '\n';

    return 0;
}

Output yang Dihasilkan:

text
is_genap(4) = 1
desc: 9 8 5 3 2 1 
jumlah di atas 5 = 2
sum via capture [&] = 28
sum(x * 3) = 84
add_base(7) = 107
next(): 1 2 3
counter (outer) = 0
max(3, 7)   = 7
max(2.5, 1.5) = 2.5
max("abc", "abd") = abd
half(5) = 2.5
op(3,4) = 7

Bedah Kode Baris-per-Baris #

  1. auto is_genap = [](int x) { return x % 2 == 0; };: lambda tanpa capture, mengembalikan bool. Disimpan ke auto — tipe closure tidak bisa ditulis manual. Pemanggilan is_genap(4) mengembalikan 1 (true).
  2. std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; }): comparator lambda inline — descending. Lambda tanpa capture bisa dikonversi ke function pointer, cocok untuk parameter algoritma STL.
  3. [threshold](int x) { return x > threshold; }: capture threshold by-value. Snapshot nilai threshold = 5 diambil saat lambda dibuat. Bila threshold diubah kemudian, lambda tetap memakai 5.
  4. [&sum](int x) { sum += x; }: capture sum by-reference. Lambda memodifikasi sum langsung. Setelah for_each, sum == 28 (jumlah 9+8+5+3+2+1).
  5. [factor, &acc](int x) { acc += x * factor; }: mixed capture — factor by-value, acc by-reference. Setelah for_each, acc == 84 = 3 × 28.
  6. [=](int x) { return x + base; }: default capture [=] menangkap semua variabel luar by-value. base (100) di-snapshot.
  7. [counter]() mutable { return ++counter; }: lambda mutable mengizinkan modifikasi capture by-value. Setiap pemanggilan menambah counter di dalam closure, tetapi counter luar tetap 0. Tiga pemanggilan menghasilkan 1, 2, 3.
  8. [](auto a, auto b) { return a > b ? a : b; }: generic lambda (C++14). auto di parameter membuatnya bekerja untuk berbagai tipe: int, double, const char*. Untuk "abc" vs "abd", hasilnya "abd" (pointer comparison via operator>).
  9. [](int x) -> double { return x / 2.0; }: trailing return type -> double memaksa tipe balikan double. Tanpa annotation, x / 2.0 sudah menghasilkan double — di sini annotation menjelaskan intent.
  10. std::function<int(int,int)> op = ...: simpan lambda di std::function. Berguna bila lambda perlu disimpan di member kelas atau dilewat antar fungsi. Konsekuensi: ada overhead virtual call + kemungkinan alokasi heap.

Pola Umum di DSA #

Comparator untuk std::sort #

cpp
std::sort(v.begin(), v.end(),
          [](const Item& a, const Item& b) { return a.priority > b.priority; });

Akumulator dengan Capture by-reference #

cpp
long long total = 0;
std::for_each(v.begin(), v.end(), [&total](int x) { if (x > 0) total += x; });

Callback Generik #

cpp
template <typename F>
void for_each_item(const std::vector<Item>& v, F callback) {
    for (const auto& it : v) callback(it);
}
// Pemanggilan: for_each_item(items, [](const Item& i) { std::cout << i.name; });

Ringkasan #

  • Sintaks: [capture](params) mutable -> ret { body }.
  • Capture mode: [] (tidak ada), [=] (semua by-value), [&] (semua by-ref), [x]/[&x] (selektif), [=, &x] (mixed).
  • Lambda tanpa capture bisa dikonversi ke function pointer; lambda dengan capture tidak.
  • mutable mengizinkan modifikasi capture by-value — state lokal per closure.
  • Generic lambda (C++14): auto parameter, satu lambda bekerja untuk banyak tipe.
  • std::function menyimpan lambda generik tetapi dengan overhead; auto lebih cepat bila tipe tidak perlu diabstraksi.
  • Hati-hati dangling reference bila menyimpan lambda capture by-reference ke jangka panjang.