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 #
[ 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 darireturn.{ body }: tubuh lambda.
Mode Capture #
| Sintaks | Arti |
|---|---|
[] | 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 #
#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:
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) = 7Bedah Kode Baris-per-Baris #
auto is_genap = [](int x) { return x % 2 == 0; };: lambda tanpa capture, mengembalikanbool. Disimpan keauto— tipe closure tidak bisa ditulis manual. Pemanggilanis_genap(4)mengembalikan1(true).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.[threshold](int x) { return x > threshold; }: capturethresholdby-value. Snapshot nilaithreshold = 5diambil saat lambda dibuat. Bilathresholddiubah kemudian, lambda tetap memakai5.[&sum](int x) { sum += x; }: capturesumby-reference. Lambda memodifikasisumlangsung. Setelahfor_each,sum == 28(jumlah9+8+5+3+2+1).[factor, &acc](int x) { acc += x * factor; }: mixed capture —factorby-value,accby-reference. Setelahfor_each,acc == 84= 3 × 28.[=](int x) { return x + base; }: default capture[=]menangkap semua variabel luar by-value.base(100) di-snapshot.[counter]() mutable { return ++counter; }: lambda mutable mengizinkan modifikasi capture by-value. Setiap pemanggilan menambahcounterdi dalam closure, tetapicounterluar tetap0. Tiga pemanggilan menghasilkan1, 2, 3.[](auto a, auto b) { return a > b ? a : b; }: generic lambda (C++14).autodi parameter membuatnya bekerja untuk berbagai tipe:int,double,const char*. Untuk"abc"vs"abd", hasilnya"abd"(pointer comparison viaoperator>).[](int x) -> double { return x / 2.0; }: trailing return type-> doublememaksa tipe balikandouble. Tanpa annotation,x / 2.0sudah menghasilkandouble— di sini annotation menjelaskan intent.std::function<int(int,int)> op = ...: simpan lambda distd::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 #
std::sort(v.begin(), v.end(),
[](const Item& a, const Item& b) { return a.priority > b.priority; });Akumulator dengan Capture by-reference #
long long total = 0;
std::for_each(v.begin(), v.end(), [&total](int x) { if (x > 0) total += x; });Callback Generik #
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.
mutablemengizinkan modifikasi capture by-value — state lokal per closure.- Generic lambda (C++14):
autoparameter, satu lambda bekerja untuk banyak tipe. std::functionmenyimpan lambda generik tetapi dengan overhead;autolebih cepat bila tipe tidak perlu diabstraksi.- Hati-hati dangling reference bila menyimpan lambda capture by-reference ke jangka panjang.