Selama Section ini kita mengakses data komposit satu per satu: produk.nama, produk.stok, peringkat.first, dan seterusnya. C++17 menyediakan cara yang lebih ringkas dan menyenangkan untuk membongkar isi tipe komposit: structured bindings, dieja auto [a, b, c] = .... Satu pernyataan memperkenalkan beberapa nama sekaligus, masing-masing terikat pada satu bagian dari objek yang diurai — seperti membuka kotak berisi tiga item dan langsung meletakkan ketiganya di meja dengan label masing-masing. Pada Chapter ini kita mencoba structured bindings pada empat tipe yang berbeda, memahami aturan penamaannya, dan membedakan binding salinan dengan binding referensi.
Mengurai Tiga Jenis Objek dalam Satu Pernyataan #
Structured bindings bekerja pada tipe yang “bagian-bagiannya diketahui saat kompilasi”: struct, std::pair (pasangan dua nilai dari tipe yang boleh berbeda — akan dibahas tuntas pada Chapter pair dan tuple di Section Standard Library), std::tuple (perluasannya untuk sejumlah nilai), dan std::array (array berukuran tetap dari Section Array dan String). Untuk struct, urutan binding mengikuti urutan deklarasi member — aturan yang sama dengan aggregate initialization di chapter awal Section ini.
#include <array>
#include <iostream>
#include <string>
#include <tuple>
#include <utility>
struct NilaiUjian {
std::string nama;
double rataRata;
int jumlahSks;
};
int main() {
// 1. struct: binding mengikuti urutan member
NilaiUjian ujian {"Budi", 87.5, 24};
auto [nama, rata, sks] = ujian;
std::cout << nama << ": rata-rata " << rata << ", " << sks << " SKS\n";
// 2. std::pair: pasangan dua nilai
std::pair<int, std::string> peringkat {1, "Siti"};
auto [posisi, namaMhs] = peringkat;
std::cout << "Peringkat " << posisi << ": " << namaMhs << "\n";
// 3. std::tuple: deretan nilai tipe campuran
std::tuple<std::string, int, double> produk {"Kopi Arabika", 24, 55000.0};
auto [jenis, stok, harga] = produk;
std::cout << jenis << " stok " << stok << ", Rp" << harga << "\n";
// 4. std::array: array berukuran tetap
std::array<int, 3> suhu {28, 31, 30};
auto [pagi, siang, sore] = suhu;
std::cout << "Pagi " << pagi << ", siang " << siang << ", sore " << sore << "\n";
// Binding by value adalah salinan: mengubahnya tidak menyentuh aslinya
auto [kopiNama, kopiRata, kopiSks] = ujian;
kopiNama = "Anonim";
std::cout << "Binding diubah jadi " << kopiNama
<< ", asli tetap " << ujian.nama << "\n";
// const auto&: tanpa salinan, cocok untuk membaca objek besar
const auto& [n, r, jml] = ujian;
std::cout << "Referensi: " << n << " rata-rata " << r << ", " << jml << " SKS\n";
return 0;
}Output yang Dihasilkan:
Budi: rata-rata 87.5, 24 SKS
Peringkat 1: Siti
Kopi Arabika stok 24, Rp55000
Pagi 28, siang 31, sore 30
Binding diubah jadi Anonim, asli tetap Budi
Referensi: Budi rata-rata 87.5, 24 SKSMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 15-17 (
auto [nama, rata, sks] = ujian;): tiga nama baru diperkenalkan sekaligus.namaterikat pada membernama(bertipestd::string),ratapadarataRata(bertipedouble), danskspadajumlahSks— urutan dan tipe mengikuti definisi struct. - Baris 20-22 (std::pair):
peringkatmenyimpanintdanstd::string; binding memperkenalkanposisidannamaMhs..first/.secondtidak perlu ditulis lagi. - Baris 25-27 (std::tuple): tuple tiga elemen diurai menjadi tiga nama. Sintaksnya identik dengan pair — compiler tahu berapa banyak bagiannya dari tipe itu sendiri.
- Baris 30-32 (std::array): tiga elemen array diurai menjadi
pagi,siang, dansore. Jumlah nama wajib sama dengan ukuran array. - Baris 35-38:
autotanpa&membuat salinan —kopiNamaadalah string baru yang kepalanya salinan dariujian.nama. MengubahkopiNamamenjadi"Anonim"tidak menyentuhujian, persis seperti perilaku pass-by-value pada Section Fungsi. - Baris 41-42 (
const auto& [n, r, jml] = ujian;): binding berupa referenceconst— tidak ada penyalinan, dan binding tidak boleh dipakai mengubah aslinya. Ini pilihan terbaik untuk membaca objek besar atau parameter fungsi.
Aturan Nama Binding #
Structured bindings punya beberapa aturan yang perlu dipegang supaya tidak tersandung. Yang pertama dan paling sering mengusir pemula: jumlah nama harus persis sama dengan jumlah bagian. Tidak bisa mengurai tiga member struct dengan hanya dua nama:
NilaiUjian ujian {"Budi", 87.5, 24};
auto [nama, rata] = ujian; // ditolak: struct punya 3 member
g++ 14.2 menolak kode di atas dengan pesan yang lugas:
error: only 2 names provided for structured binding
note: while 'NilaiUjian' decomposes into 3 elementsAturan lainnya melengkapi gambaran: nama-nama binding diperkenalkan sekaligus dalam satu pernyataan — tidak ada cara membongkar setengahnya saja atau mendeklarasikan ulang satu nama di pernyataan berikutnya. Nama juga tidak boleh membayang-bayangi (shadow) variabel lain di scope yang sama, dan biasannya memakai auto karena tipe setiap bagian dihitung compiler — pola yang sudah akrab sejak Chapter auto dan Type Deduction.
Salinan vs Referensi: Memilih yang Tepat #
Ketiga bentuk yang sering dipakai dapat kita petakan seperti tabel pass-by-value versus pass-by-reference:
auto [a, b] = obj;— salinan penuh; mengubah binding aman dan tidak menyentuh objek asal. Cocok untuk objek kecil atau bila binding mau dimodifikasi.auto& [a, b] = obj;— reference biasa; mengubah binding berarti mengubah objek asalnya.const auto& [a, b] = obj;— referenceconst; baca saja tanpa biaya salinan. Pilihan bawaan yang terbaik untuk membaca struct besar di dalam loop atau fungsi.
Kombinasi dengan range-based for adalah pola yang paling sering kita temui di kode modern: for (const auto& [nama, stok] : daftarProduk) mengurai setiap elemen vector of struct tanpa menyalin apa pun — penerapan langsung dari Chapter Range-Based for. Dengan itu, perlengkapan kita untuk tipe komposit lengkap: struct untuk mengelompokkan, enum class untuk membatasi pilihan, union untuk berbagi memori secara terkendali, alias untuk menamai, dan structured bindings untuk mengurai.
Referensi resmi untuk fitur ini dapat dibaca pada halaman structured binding declaration di cppreference. Section berikutnya, Pemrograman Berorientasi Objek, mengambil struct yang kita kuasai ini satu langkah lebih jauh: class yang menyatukan data dengan perilaku yang mengoperasikannya.