Menu
CoddyTech

Ekspresi Lambda secara Mendalam

Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 85 dari 104.

Ekspresi lambda, yang diperkenalkan di C++11, adalah fungsi anonim yang bisa kamu definisikan secara langsung. Kamu sudah melihat lambda dasar dengan algoritma STL, tetapi memahami sintaksnya secara lengkap membuka kemampuan canggih untuk menangkap variabel dan mengendalikan cara variabel tersebut diakses.

Sintaks lengkap lambda adalah: [capture](parameters) mutable -> return_type { body }. Klausa capture menentukan variabel luar mana yang dapat diakses lambda dan caranya:

#include <iostream>

int main() {
    int x = 10;
    int y = 20;
    
    auto byValue = [x]() { return x * 2; };        // Salinan x
    auto byRef = [&y]() { y += 5; };               // Referensi ke y
    auto allByValue = [=]() { return x + y; };     // Salin semua
    auto allByRef = [&]() { x++; y++; };           // Referensi ke semua
    auto mixed = [x, &y]() { y += x; };            // Gabungkan keduanya
    
    byRef();
    std::cout << y << "\n";  // 25
}

Secara default, variabel yang ditangkap berdasarkan nilai bersifat const di dalam lambda. Kata kunci mutable memungkinkan salinan ini diubah:

int counter = 0;
auto increment = [counter]() mutable {
    return ++counter;  // Mengubah salinan milik lambda
};

std::cout << increment() << "\n";  // 1
std::cout << increment() << "\n";  // 2
std::cout << counter << "\n";      // 0 - nilai asli tidak berubah

C++14 menambahkan init capture, sehingga kamu dapat membuat variabel baru atau memindahkan object ke dalam lambda:

auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
    return *p;
};  // Kepemilikan dipindahkan ke lambda

Lambda sangat berguna dalam OOP saat kamu perlu meneruskan perilaku sebagai parameter, misalnya untuk callback, comparator kustom, atau event handler, tanpa mendefinisikan object fungsi terpisah.

challenge icon

Tantangan

Mudah

Mari buat sistem penanganan event yang menunjukkan keunggulan ekspresi lambda dengan berbagai mode penangkapan. Kamu akan membuat dispatcher event sederhana yang menyimpan dan memanggil callback, untuk menunjukkan cara lambda menangkap state eksternal dengan berbagai cara.

Kamu akan menyusun kodemu dalam tiga file:

  • EventDispatcher.h: Definisikan class EventDispatcher yang mengelola callback event.

    Dispatcher-mu harus menyimpan callback menggunakan std::vector dari std::function<void()>. Sertakan method berikut:

    • addCallback(std::function<void()> callback): menambahkan callback ke daftar
    • fireAll(): memanggil semua callback yang tersimpan secara berurutan
    • clear(): menghapus semua callback

    Kamu perlu menyertakan <functional> dan <vector>.

  • EventDispatcher.cpp: Implementasikan method untuk dispatcher-mu. Method fireAll() cukup melakukan iterasi pada semua callback dan memanggilnya satu per satu.
  • main.cpp: Baca dua input:
    1. Angka dasar (integer)
    2. Pengali (integer)

    Buat EventDispatcher dan tunjukkan berbagai teknik penangkapan lambda dengan menambahkan tiga callback:

    1. Lambda yang menangkap angka dasar berdasarkan nilai dan mencetak: Base value: [base]
    2. Lambda yang menangkap pengali berdasarkan referensi, menambah nilainya sebesar 1, lalu mencetak: Multiplier after increment: [multiplier]
    3. Lambda mutable yang menangkap variabel penghitung (diinisialisasi ke 0) berdasarkan nilai, menambah nilainya setiap kali dipanggil, dan mencetak: Call count: [counter]

    Setelah menambahkan semua callback, panggil fireAll() dua kali untuk melihat perilaku berbagai mode penangkapan dalam beberapa pemanggilan. Di antara kedua pemanggilan fireAll(), cetak --- sebagai pemisah.

    Terakhir, setelah kedua putaran, cetak nilai akhir variabel pengali dari main untuk menunjukkan pengaruh penangkapan berdasarkan referensi: Final multiplier: [multiplier]

Contohnya, dengan input 10 dan 5:

Base value: 10
Multiplier after increment: 6
Call count: 1
---
Base value: 10
Multiplier after increment: 7
Call count: 1
Final multiplier: 7

Dengan input 42 dan 0:

Base value: 42
Multiplier after increment: 1
Call count: 1
---
Base value: 42
Multiplier after increment: 2
Call count: 1
Final multiplier: 2

Perhatikan perilaku utamanya: penangkapan berdasarkan nilai mempertahankan angka dasar awal, penangkapan berdasarkan referensi mengubah variabel pengali yang sebenarnya di main (nilainya terus bertambah setiap kali dipanggil), dan penghitung lambda mutable bernilai 1 pada setiap putaran. Ini karena fireAll() melakukan iterasi pada callback berdasarkan nilai: setiap putaran memanggil salinan baru dari std::function yang tersimpan. Penghitung yang ditangkap dalam salinan tersebut mulai dari 0 dan bertambah menjadi 1, sehingga state callback yang tersimpan tidak pernah bertambah.

Coba sendiri

#include <iostream>
#include "EventDispatcher.h"

using namespace std;

int main() {
    int base;
    int multiplier;
    cin >> base;
    cin >> multiplier;
    
    EventDispatcher dispatcher;
    
    // TODO: Tambahkan lambda yang menangkap base BERDASARKAN NILAI
    // Lambda tersebut harus mencetak: "Base value: [base]"
    
    // TODO: Tambahkan lambda yang menangkap multiplier BERDASARKAN REFERENSI
    // Lambda tersebut harus menaikkan multiplier sebesar 1, lalu mencetak: "Multiplier after increment: [multiplier]"
    
    // TODO: Tambahkan lambda MUTABLE yang menangkap counter (diinisialisasi ke 0) berdasarkan nilai
    // Lambda tersebut harus menaikkan counter dan mencetak: "Call count: [counter]"
    
    // TODO: Panggil fireAll() untuk menjalankan semua callback
    
    // TODO: Cetak "---" sebagai pemisah
    
    // TODO: Panggil fireAll() lagi
    
    // TODO: Cetak nilai akhir multiplier: "Final multiplier: [multiplier]"
    
    return 0;
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Object

Latihan sendiri: Compiler C++ online