Hampir semua loop iterator dan semua algoritma STL berdiri di atas dua titik: begin() yang menunjuk elemen pertama dan end() yang menandai batas akhir penelusuran. Pasangan ini menggambarkan rentang setengah terbuka [begin, end) — elemen pertama termasuk, elemen yang ditunjuk end() tidak. Chapter ini membahas dua bentuk pemanggilan yang tersedia, yaitu member function milik container dan free function std::begin/std::end, keunggulan masing-masing, dukungan terhadap array C-style, serta varian cbegin/cend untuk akses read-only. Kita juga membongkar makna end() yang sering disalahpahami: ia bukan elemen terakhir, melainkan satu langkah setelahnya.
Member Function vs Free Function #
Setiap container standar menyediakan member begin() dan end(), misalnya v.begin() pada std::vector. Selain itu, header <iterator> menyediakan free function std::begin(v) dan std::end(v) yang, untuk container, hanya memanggil member yang sama sehingga hasilnya identik. Lalu apa bedanya dan kapan masing-masing lebih tepat?
Keunggulan free function terletak pada satu hal penting: ia bekerja juga untuk array C-style mentah yang tidak punya member function. std::begin(arr) pada int arr[5] mengembalikan pointer int* ke elemen pertama, dan std::end(arr) mengembalikan pointer arr + 5. Dengan begitu, kode berbasis iterator tetap bisa ditulis tanpa peduli data disimpan di std::vector atau array biasa. Free function juga menjadi titik ekstensi: kita bisa menyediakan overload begin/end untuk tipe buatan sendiri sehingga tipe kita bisa ikut memakai algoritma STL.
#include <iostream>
#include <iterator>
#include <vector>
int main() {
std::vector<int> data = {5, 10, 15};
// Member function: data.begin() dan data.end()
for (auto it = data.begin(); it != data.end(); ++it) {
if (it != data.begin()) std::cout << ' ';
std::cout << *it;
}
std::cout << '\n';
// Free function std::begin/std::end: bekerja juga untuk array C-style
int arr[] = {2, 4, 6, 8};
for (auto it = std::begin(arr); it != std::end(arr); ++it) {
if (it != std::begin(arr)) std::cout << ' ';
std::cout << *it;
}
std::cout << '\n';
// end - begin = jumlah elemen (hanya untuk iterator random access)
std::cout << "Jumlah elemen arr: " << (std::end(arr) - std::begin(arr)) << '\n';
// cbegin/cend: iterator const, elemen tidak boleh diubah
for (auto it = data.cbegin(); it != data.cend(); ++it) {
if (it != data.cbegin()) std::cout << ' ';
std::cout << *it;
}
std::cout << '\n';
// Pola loop kosong: pada container kosong, begin() == end()
// sehingga badan loop tidak pernah dijalankan
std::vector<int> kosong;
int hitung = 0;
for (auto it = kosong.begin(); it != kosong.end(); ++it) {
++hitung;
}
std::cout << "Loop pada vector kosong berjalan: " << hitung << " kali\n";
return 0;
}Output yang Dihasilkan:
5 10 15
2 4 6 8
Jumlah elemen arr: 4
5 10 15
Loop pada vector kosong berjalan: 0 kaliBedah baris demi baris:
- Baris 9-13: iterasi dengan member
data.begin()dandata.end(). Pembandingit != data.begin()dipakai hanya untuk memberi spasi pemisah sebelum setiap angka setelah yang pertama. - Baris 17-21: iterasi array C-style memakai
std::begin(arr)danstd::end(arr). Sintaksnya sama persis dengan container — inilah manfaat utama free function. - Baris 24: selisih
std::end(arr) - std::begin(arr)menghasilkan jumlah elemen, yaitu4. Operasi pengurangan iterator hanya sah untuk kategori random access, dan pointer array termasuk kategori ini. - Baris 28-32:
cbegin()dancend()mengembalikan iterator const; elemen tetap terbaca, tetapi penulisan*it = ...akan ditolak compiler. - Baris 37-42: pada vector kosong,
begin()danend()bernilai sama sehingga loop berjalan nol kali tanpa perlu pemeriksaan khusus — pola aman yang berlaku untuk semua container.
end() = One Past the Last #
Makna end() adalah titik yang paling sering ditanyakan pemula: end() tidak menunjuk elemen apa pun, melainkan satu langkah setelah elemen terakhir (one past the last). Dereference end() adalah undefined behavior — kita hanya boleh membandingkannya, bukan membacanya. Desain ini sengaja dipilih karena membuat rentang setengah terbuka [begin, end) memiliki sifat yang rapi:
- Rentang kosong dinyatakan dengan satu kondisi saja:
begin() == end(). Tidak ada kasus khusus. - Jumlah elemen dihitung sebagai
end() - begin()pada iterator random access, tanpa penyesuaian plus-minus satu. - Algoritma yang menambahkan elemen di posisi
end()— misalnyastd::copydengan tujuanback_inserter— punya tempat alami untuk menulis.
Memilih Bentuk yang Tepat #
Sebagai aturan praktis: di dalam loop dan kode umum, pakai bentuk yang paling ringkas dan jelas. Member v.begin() lazim untuk kode container tunggal, sedangkan std::begin(v)/std::end(v) lebih tepat ketika kita menulis kode generik yang harus menerima array C-style maupun container. Untuk data yang tidak boleh berubah, pilih cbegin()/cend() (atau member pada object const) agar compiler menjadi penjaga — setiap usaha mengubah elemen melalui iterator const langsung menjadi compile error.
Dengan begin dan end sudah kokoh, kita siap membawa pasangan ini ke tingkat berikutnya: menyerapkannya ke algoritma siap pakai dari header <algorithm>, yang pola pemakaiannya kita bahas pada chapter berikutnya.
Rujukan resmi: std::begin, std::end dan member begin/end pada vector .