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 sintaks lengkapnya membuka kemampuan canggih untuk menangkap variabel dan mengontrol cara variabel tersebut diakses.

Sintaks lambda yang lengkap 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 captures, yang memungkinkan kamu 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 dialihkan ke lambda

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

challenge icon

Tantangan

Mudah

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

Kamu akan mengatur kodemu dalam tiga file:

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

    Dispatcher-mu harus menyimpan callback menggunakan std::vector berisi 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 mengiterasi semua callback dan memanggil masing-masing.
  • main.cpp: Baca dua input:
    1. Angka basis (integer)
    2. Pengali (integer)

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

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

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

    Terakhir, setelah kedua putaran, cetak nilai akhir variabel pengali dari main untuk menunjukkan pengaruh capture referensi terhadapnya: 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: capture berdasarkan nilai mempertahankan nilai awal basis, capture berdasarkan referensi mengubah variabel pengali yang sebenarnya di main (nilainya terus bertambah setiap kali dipanggil), dan penghitung lambda mutable menunjukkan 1 di setiap putaran. Itu karena fireAll() mengiterasi callback-nya berdasarkan nilai: setiap putaran memanggil salinan baru dari std::function yang tersimpan. Penghitung yang di-capture salinan tersebut dimulai 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 ini harus mencetak: "Base value: [base]"
    
    // TODO: Tambahkan lambda yang menangkap multiplier BERDASARKAN REFERENSI
    // Lambda ini harus menaikkan multiplier sebesar 1, lalu mencetak: "Multiplier after increment: [multiplier]"
    
    // TODO: Tambahkan lambda MUTABLE yang menangkap penghitung (diinisialisasi ke 0) berdasarkan nilai
    // Lambda ini harus menaikkan penghitung 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