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 berubahC++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 lambdaLambda sangat berguna dalam OOP saat kamu perlu meneruskan perilaku sebagai parameter, misalnya untuk callback, comparator kustom, atau event handler, tanpa mendefinisikan object fungsi terpisah.
Tantangan
MudahMari 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 classEventDispatcheryang mengelola callback event.Dispatcher-mu harus menyimpan callback menggunakan
std::vectordaristd::function<void()>. Sertakan method berikut:addCallback(std::function<void()> callback): menambahkan callback ke daftarfireAll(): memanggil semua callback yang tersimpan secara berurutanclear(): menghapus semua callback
Kamu perlu menyertakan
<functional>dan<vector>.EventDispatcher.cpp: Implementasikan method untuk dispatcher-mu. MethodfireAll()cukup melakukan iterasi pada semua callback dan memanggilnya satu per satu.main.cpp: Baca dua input:- Angka dasar (integer)
- Pengali (integer)
Buat
EventDispatcherdan tunjukkan berbagai teknik penangkapan lambda dengan menambahkan tiga callback:- Lambda yang menangkap angka dasar berdasarkan nilai dan mencetak:
Base value: [base] - Lambda yang menangkap pengali berdasarkan referensi, menambah nilainya sebesar 1, lalu mencetak:
Multiplier after increment: [multiplier] - 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 pemanggilanfireAll(), 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: 7Dengan 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: 2Perhatikan 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;
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Object
1Dasar-Dasar OOP
File EksternalBuild dan Kompilasi C++File Header dan File SourceNamespace dan CakupanPengantar OOP dalam C++class vs objectPointer 'this'method (Fungsi Anggota)Atribut (Anggota Data)Dasar Ctor dan DtorRingkasan: Kalkulator Sederhana4Properti class
Anggota instance vs staticGetter dan setterFungsi anggota constKata kunci mutableMethod dan variabel staticFungsi dan class friendRangkuman, Pengelola Rekening Bank7Pewarisan
Pewarisan DasarTingkat Akses pada PewarisanUrutan Pemanggilan Ctor & DtorPenimpaan methodFungsi Virtual & VTablePewarisan BergandaPewarisan VirtualRangkuman: Hierarki Karyawan2Manajemen Memori
Memori Stack vs HeapPointer dan ReferenceMemori Dinamis (new/delete)Smart Pointer di C++RAII di C++Rangkuman: Pengelola Array Dinamis5Enkapsulasi
Penentu Akses dalam C++Penentu Akses secara MendalamPenyembunyian Informasistruct vs classNested dan Inner classRangkuman, Sistem Catatan Siswa8Polimorfisme
Polimorfisme saat Compile vs RuntimeOverloading FungsiMeninjau Kembali Fungsi VirtualFungsi Virtual MurniKelas AbstrakDesain Interface di C++Casting Dinamis & RTTIRangkuman: Kalkulator Bentuk3Konstruktor & Destruktor
Konstruktor DefaultKonstruktor BerparameterKonstruktor SalinKonstruktor PindahDaftar Inisialisasi KonstruktorKonstruktor DelegasiKupas Tuntas DestruktorAturan Tiga, Lima, dan NolRangkuman: class String6Overloading Operator
Pengantar Overloading OperatorOverloading Operator AritmetikaOverloading Operator PerbandinganOperator StreamOverloading Operator PenugasanOverloading Operator [] dan ()Operator Konversi TipeRingkasan, class Matrix9Template
Template FungsiTemplate classSpesialisasi TemplateTemplate VariadikDasar SFINAE & Type TraitsRangkuman, Container Generik12Fitur Modern C++
Semantik Pemindahan & RvaluePenerusan SempurnaEkspresi Lambda secara Mendalamstd::function & std::bindconstexpr dan constevalPengikatan Terstrukturoptional, variant, anyLatihan sendiri: Compiler C++ online