Menu

Range-based for loop C++: sintaks, auto, dan reference

Range-based for loop di C++ dijelaskan: perulangan yang rapi pada array, vector, string, dan map, kenapa kamu sebaiknya memakai auto& dan const auto&, serta jebakan penyalinan dan iterator invalidation yang perlu dihindari.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

"Untuk setiap elemen, lakukan ini"

for loop klasik yang menghitung sangat bagus kalau kamu punya indeks untuk dijalankan. Tapi sering kali kamu sebenarnya tidak peduli dengan indeksnya, kamu cuma ingin menyentuh setiap elemen container. Menulis for (int i = 0; i < v.size(); i++) untuk itu terlalu ramai, dan i hanya berjarak satu kesalahan off-by-one dari membaca di luar batas.

C++11 menambahkan range-based for loop tepat untuk keperluan ini. Kamu memberi nama variabel, menunjuk ke sebuah container, dan loop akan menelusuri setiap elemennya untukmu:

Tanpa indeks, tanpa .size(), tanpa batas yang bisa salah. Bacalah sebagai "untuk setiap s di dalam scores". Cara ini bekerja pada array biasa, std::vector, std::string, std::map, dan apa pun yang menyediakan begin() dan end().

Biarkan auto memilih tipenya

Menuliskan tipe elemennya secara lengkap memang bisa, tapi rapuh: ubah tipe container-nya, dan setiap loop juga harus diubah. Pasangkan range-based for dengan auto, maka compiler akan menentukan tipe elemennya untukmu:

Tapi ada biaya tersembunyi di sini. auto name biasa menghasilkan tipe string dan menyalin setiap elemen ke name di setiap putaran. Untuk int itu gratis; untuk string atau struct besar, itu alokasi yang sia-sia di setiap iterasi. Solusinya adalah reference, dan itulah hal berikutnya yang perlu dipahami.

Mengubah di tempat dengan auto&

Kalau kamu menulis auto x, kamu mendapat salinan, jadi mengisi nilai ke x hanya mengubah salinannya, bukan container-nya. Perhatikan jebakan ini:

Penggandaan nilai itu diam-diam tidak berpengaruh apa-apa karena n adalah salinan sekali pakai. Untuk benar-benar mengubah elemennya, ambil elemen itu sebagai reference dengan auto&:

Satu tanda & adalah seluruh perbedaan antara "lihat tapi jangan sentuh" dan "ubah di tempat". Kalau kamu pernah bertanya-tanya kenapa perubahanmu hilang, hampir selalu inilah penyebabnya.

Membaca tanpa menyalin: const auto&

Kalau kamu hanya perlu membaca elemen tapi elemennya mahal untuk disalin, pakai const auto&. Reference menghindari penyalinan, dan const mendokumentasikan (sekaligus memastikan) bahwa kamu tidak akan mengubah apa pun:

Aturan praktis yang bagus:

for (auto x : c)         // copy   - cheap types (int, char, pointers)
for (auto& x : c)        // edit   - you want to change the elements
for (const auto& x : c)  // read   - heavy types you only inspect

Gunakan const auto& sebagai default saat membaca dan auto& saat menulis. Pakai auto biasa hanya untuk tipe yang benar-benar kecil dan murah untuk disalin.

Perulangan pada map dan pair

Range-based for pada std::map memberimu std::pair untuk setiap entri, dengan .first (key) dan .second (value). Sejak C++17, structured binding memungkinkan kamu membongkar pair itu menjadi dua variabel bernama langsung di kepala loop:

[name, age] jauh lebih jelas daripada mengulang entry.first dan entry.second di mana-mana. Tetap pakai const auto& di sini juga: key dari entri map adalah string, jadi menyalin setiap pair akan sia-sia.

Jebakan: jangan mengubah ukuran saat sedang loop

Jebakan terbesarnya adalah mengubah ukuran container saat range-based for sedang menelusurinya. Memanggil push_back, erase, insert, atau clear bisa mengalokasikan ulang tempat penyimpanan di baliknya dan membuat iterator internal loop tidak valid. Hasilnya adalah undefined behavior, artinya crash atau nilai sampah, bukan pesan error yang ramah:

vector<int> v = {1, 2, 3};
for (int x : v) {
    v.push_back(x);   // UNDEFINED BEHAVIOR - reallocation invalidates the range
}

Kalau kamu perlu menambah atau menghapus elemen sambil memproses, beralihlah ke for loop berbasis indeks atau iterator dan atur batasnya sendiri, atau buat container hasil yang terpisah lalu tukar setelahnya. Ada dua jebakan kecil dari keluarga yang sama: jangan pernah mengikat range-based for ke objek sementara yang langsung mati (for (auto x : makeVector()) tidak masalah, tapi for (auto& x : someObj.getTempVector()) bisa menggantung), dan ingat bahwa for (auto& c : myString) memungkinkan kamu mengubah karakter satu per satu di tempatnya.

Berikutnya: fungsi

Range-based for loop merapikan perulangan, dan pilihan auto / auto& / const auto& yang baru kamu pelajari langsung terbawa ke salah satu alat terpenting di C++. Berikutnya kita akan membungkus logika ke dalam fungsi yang bisa dipakai ulang: memberi kode sebuah nama, parameter, dan nilai kembalian, supaya kamu bisa memanggilnya dari mana saja tanpa mengulang-ulang kode.

Pertanyaan yang sering diajukan

Apa itu range-based for loop di C++?

Range-based for loop mengunjungi setiap elemen sebuah container (array, vector, string, map, dan lain-lain) tanpa kamu perlu mengatur indeks atau iterator. Sintaksnya for (auto x : container) { ... }. Fitur ini ditambahkan di C++11 dan merupakan cara paling rapi untuk mengatakan "lakukan ini untuk setiap elemen".

Kapan sebaiknya memakai auto& alih-alih auto di range-based for loop?

Pakai auto& x kalau kamu ingin mengubah elemen di tempatnya, dan const auto& x kalau kamu hanya membaca tapi ingin menghindari penyalinan (penting untuk string, vector, atau objek besar). auto x biasa membuat salinan di setiap iterasi: tidak masalah untuk tipe murah seperti int, tapi boros untuk yang lain.

Bisakah ukuran vector diubah di dalam range-based for loop di C++?

Tidak. Memanggil push_back, erase, insert, atau clear pada container yang sedang ditelusuri membuat iterator internal loop tidak valid lagi dan termasuk undefined behavior: program bisa crash atau datanya rusak diam-diam. Kalau kamu perlu menambah atau menghapus elemen sambil melakukan perulangan, pakai for loop berbasis indeks atau iterator.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai