Chapter Structured Bindings di section Struktur dan Tipe Data Komposit sudah memperkenalkan dasarnya: satu pernyataan C++17 membongkar isi objek menjadi beberapa nama — auto [kode, pesan] = hasil; — tanpa akses .first/.second yang menggangu pembacaan. Chapter ini memperdalam tiga hal yang menjadi penentu kenyamanan sehari-hari. Pertama, kita pastikan mekanismenya: nama-nama hasil binding bukan variabel baru, melainkan nama alternatif untuk bagian dalam objek, dan konsekuensinya mengikuti aturan deduksi yang sudah kita pegang dari chapter auto dan Type Deduction. Kedua, kita bandingkan tiga bentuk modifier — auto, auto&, const auto& — dengan bukti eksekusi siapa yang menyalin dan siapa yang mengalias. Ketiga, kita bedah use case yang paling sering muncul di kode nyata: iterasi std::map dengan nama bermakna alih-alih entry.first dan entry.second.
Empat Jenis Objek yang Bisa Dibongkar #
Structured binding bekerja pada empat kategori: struct/aggregate, std::pair, std::tuple, dan array (termasuk C-array). Di belakang layar compiler memperluasnya kira-kira seperti ini:
auto [kode, pesan] = hasil;
// kurang lebih menjadi:
auto e = hasil; // objek perantara
auto& kode = e.first; // nama alternatif ke bagian dalam
auto& pesan = e.second;Objek perantara e adalah kuncinya: begitu ia dibuat dari ekspresi, umurnya diperpanjang sampai akhir scope binding — jadi auto [a, b] = fungsiYangMengembalikanPair(); aman meski sumbernya temporary. Perluasan ini juga menjelaskan dua batasan: jumlah nama harus persis sama dengan jumlah elemen (tidak ada “sisa”), dan semua nama wajib dideklarasikan sekaligus dalam satu baris. Untuk struct, urutan mengikuti urutan deklarasi member — makanya binding paling enak dipakai pada struct kecil yang membernya jelas nama dan maknanya.
Program Pendalaman #
// bindings.cpp — structured bindings: struct, tuple, array, dan iterasi map
// Kompilasi: g++ -std=c++23 -Wall -Wextra bindings.cpp -o bindings
#include <algorithm>
#include <map>
#include <print>
#include <string>
#include <tuple>
#include <vector>
struct MinMax {
int kecil;
int besar;
};
MinMax analisis(const std::vector<int>& v) {
return {*std::ranges::min_element(v), *std::ranges::max_element(v)};
}
int main() {
// 1) bongkar struct: dua kembalian tanpa variabel sementara
std::print("-- struct: dua kembalian sekaligus\n");
auto [terkecil, terbesar] = analisis({4, 9, 1, 7});
std::print("min={} max={}\n", terkecil, terbesar);
// 2) bongkar std::tuple: tiga tipe berbeda dalam satu deklarasi
std::print("-- tuple: tiga tipe dalam satu deklarasi\n");
std::tuple<int, std::string, double> hasil{200, "OK", 88.5};
auto [kode, pesan, skor] = hasil;
std::print("kode={} pesan={} skor={}\n", kode, pesan, skor);
// 3) bongkar C-array: elemen bernama tanpa indeks
std::print("-- array: elemen bernama tanpa indeks\n");
int raw[3]{5, 6, 7};
auto [a, b, c] = raw;
std::print("a={} b={} c={}\n", a, b, c);
// 4) binding by value vs binding referensi
std::print("-- by value vs reference\n");
std::pair<int, std::string> data{10, "MERAH"};
auto [angka, warna] = data; // salinan kedua elemen
angka = 99; // mengubah salinan
std::print("salinan : angka={} data.first={}\n", angka, data.first);
auto& [n, w] = data; // nama alternatif untuk member asli
n = 42; // mengubah data.first sungguhan
std::print("referensi: n={} data.first={}\n", n, data.first);
// 5) use case utama: iterasi map dengan nama bermakna
std::print("-- iterasi map\n");
std::map<std::string, int> stok{{"beras", 9}, {"gandum", 3}, {"gula", 5}};
for (const auto& [nama, jumlah] : stok) {
std::print("{} = {}\n", nama, jumlah);
}
return 0;
}Output yang Dihasilkan:
-- struct: dua kembalian sekaligus
min=1 max=9
-- tuple: tiga tipe dalam satu deklarasi
kode=200 pesan=OK skor=88.5
-- array: elemen bernama tanpa indeks
a=5 b=6 c=7
-- by value vs reference
salinan : angka=99 data.first=10
referensi: n=42 data.first=42
-- iterasi map
beras = 9
gandum = 3
gula = 5Mari kita bedah:
- Baris 23-24 (dua kembalian): fungsi
analisismengembalikan structMinMaxberisi nilai terkecil dan terbesar dalam satu kembalian — binding langsung memasangkannya keterkecildanterbesar. Pola “struct kecil sebagai kembalian” menggantikan trik lama parameter output (int& minOut), dan lebih enak dibaca di sisi pemanggil. - Baris 28-30 (tuple tiga tipe):
auto [kode, pesan, skor] = hasil;membongkarint,std::string, dandoubledalam satu deklarasi — tiap nama otomatis bertipe elemen posisinya. Ini pasangan alamistd::make_tuplesaat kita belum ingin menulis struct khusus. - Baris 34-36 (C-array):
auto [a, b, c] = raw;memberi nama ke tiga elemen array polos — compiler tahu panjang array sehingga jumlah nama bisa diperiksa saat kompilasi. - Baris 40-47 (by value vs referensi):
auto [angka, warna] = data;menyalin kedua elemen, sehinggaangka = 99tidak menyentuhdata(tercetakdata.first=10). Sementaraauto& [n, w] = data;membuatnmenjadi nama alternatifdata.first—n = 42mengubah objek aslinya, terbukti daridata.first=42. Aturannya identik dengan tabel deduksiautopada chapter deduksi: by value menyalin,&mengalias. - Baris 51-54 (iterasi map): setiap elemen
std::mapadalahstd::pair<const std::string, int>, sehinggaconst auto& [nama, jumlah]mengikat key dan value ke dua nama yang bermakna. Output terurut alfabetis karenastd::mapmenjaga urutan key — perilaku yang sudah kita andalkan saat mencetak data terurut.
Aturan Modifier: Salin, Alias, atau Baca-saja #
Tiga bentuk binding mencakup hampir semua kebutuhan, dan pilihannya bisa dipandu pertanyaan sederhana: apakah kita akan membaca atau mengubah?
| Bentuk | Efek | Pakai saat |
|---|---|---|
auto [a, b] = obj; | salinan elemen; perubahan tidak menyentuh obj | butuh kepemilikan sendiri / objek kecil |
auto& [a, b] = obj; | alias ke elemen asli; bisa mengubah | akan memodifikasi data sumber |
const auto& [a, b] = obj; | alias baca-saja tanpa salinan | pilihan default untuk membaca |
Baris ketiga adalah default yang paling sering benar: iterasi map di atas memakainya karena kita hanya membaca, dan const di dalam tipe pair juga mencegah key std::map terubah — key yang berubah akan merusak urutan internal container. Bila kita memakai auto& untuk membaca tanpa mengubah, pembaca kode jadi bertanya-tanya apakah data akan dimodifikasi; sedangkan auto polos pada elemen berat (std::string, std::vector) berarti salinan penuh yang sering tidak disengaja — perilaku yang sama dengan auto biasa yang sudah kita bahas.
Use Case Utama: Iterasi map dan Kembalian Ganda #
Dua pola di program ini mewakili mayoritas pemakaian structured bindings di kode produksi. Yang pertama sudah kita lihat: iterasi std::map/std::unordered_map. Bandingkan bentuk lama for (const auto& entry : stok) std::print(entry.first, entry.second); — pembaca harus mengingat bahwa first artinya key dan second artinya value. Dengan const auto& [nama, jumlah], makna terbaca dari namanya sendiri, dan kesalahan tertukar key-value praktis mustahil terjadi tanpa terlihat.
Yang kedua adalah kembalian ganda. Sebelum C++17, fungsi yang ingin mengembalikan dua nilai terpaksa memilih antara parameter output, array yang tipe samanya, atau std::pair yang diakses .first/.second. Sekarang rantainya mulus: struct kecil (atau std::tuple) sebagai kembalian, binding di sisi pemanggil, dan tuple std::tie-style untuk mengabaikan elemen tertentu (auto [kode, pesan, skor] yang tidak dipakai tetap harus diberi nama — tak ada mekanisme _ bawaan, jadi namai dengan jelas). Kombinasi ini mengubah cara kita mendesain API kecil: fungsi bebas kaya informasi tanpa memaksa pemanggil berurusan dengan tipe antara.
Dari sini, perjalanan section Modern C++ melaju ke arah kontrol yang lebih presisi: chapter berikutnya membahas if constexpr, pernyataan percabangan yang dievaluasi saat kompilasi — alat yang pada program lambda bab lalu sudah kita lihat sekilas memilih jalur kode berdasarkan tipe.
Rujukan resmi: cppreference — structured binding declaration .