Keluarga algoritma penyalin adalah salah satu yang paling sering dipakai: std::copy menyalin seluruh rentang, copy_if menyalin yang lolos predikat, dan copy_n menyalin sejumlah elemen tertentu. Ketiganya punya satu syarat yang menjadi sumber bug klasik pemula: algoritma tidak akan memperbesar container tujuan — kita yang harus menyiapkan tempatnya, atau memakai std::back_inserter agar tujuan tumbuh otomatis. Selain menyalin, C++ juga punya algoritma std::move yang memindahkan elemen dengan menyerahkan isi objek sumber, menghindari penyalinan yang mahal. Chapter ini membahas semua itu sekaligus meluruskan kebingungan antara algoritma std::move dan utilitas std::move yang sama namanya.
std::copy dan Syarat Tujuan #
std::copy(first, last, d_first) menulis elemen sumber satu per satu ke posisi tujuan mulai dari d_first, menggeser d_first setiap kali menulis. Yang tidak dilakukannya: memeriksa apakah tujuan cukup besar. Bila tujuan adalah vector kosong dan kita mengirim v.begin() vector itu sebagai d_first, program menulis di luar batas — undefined behavior. Dua cara sah menyediakan tempat: buat tujuan dengan ukuran cukup (std::vector<int> salinan(6);), atau pakai back_inserter yang dibahas di bagian berikutnya.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
void cetak(const std::vector<int>& v) {
for (std::size_t i = 0; i < v.size(); ++i) {
if (i > 0) std::cout << ' ';
std::cout << v[i];
}
std::cout << '\n';
}
int main() {
std::vector<int> sumber = {1, 2, 3, 4, 5, 6};
// std::copy: salin [first, last) ke tujuan yang SUDAH cukup besar
std::vector<int> salinan(6);
std::copy(sumber.begin(), sumber.end(), salinan.begin());
std::cout << "copy : ";
cetak(salinan);
// copy_if + back_inserter: tujuan tumbuh otomatis
std::vector<int> genap;
std::copy_if(sumber.begin(), sumber.end(), std::back_inserter(genap),
[](int x) { return x % 2 == 0; });
std::cout << "copy_if : ";
cetak(genap);
// copy_n: salin tepat n elemen dari awal
std::vector<int> tiga;
std::copy_n(sumber.begin(), 3, std::back_inserter(tiga));
std::cout << "copy_n : ";
cetak(tiga);
// std::move (algorithm): pindahkan elemen tanpa menyalin isinya
std::vector<std::string> pesan = {"halo", "dunia"};
std::vector<std::string> tujuan(2);
std::move(pesan.begin(), pesan.end(), tujuan.begin());
std::cout << "move : " << tujuan[0] << ' ' << tujuan[1] << "\n";
std::cout << "sumber : "
<< (pesan[0].empty() ? "kosong" : pesan[0]) << "\n";
return 0;
}Output yang Dihasilkan:
copy : 1 2 3 4 5 6
copy_if : 2 4 6
copy_n : 1 2 3
move : halo dunia
sumber : kosongBedah baris demi baris:
- Baris 17-19 (
std::vector<int> salinan(6)lalustd::copy): tujuan dibuat berisi enam elemen terlebih dahulu, lalucopymenimpanya. Pola “siapkan tempat dulu” ini sah dan efisien bila ukuran sudah diketahui. - Baris 23-25 (
std::back_inserter(genap)):back_inserteradalah iterator output yang setiap kali ditulis memanggilpush_backpada container tujuan, sehingga vector tumbuh sesuai jumlah elemen yang lolos predikat. Header yang dibutuhkan:<iterator>. - Baris 24 (
copy_ifdengan lambdax % 2 == 0): hanya elemen genap yang tersalin, menghasilkan2 4 6tanpa perlu menghitung dulu berapa banyak. - Baris 29-30 (
std::copy_n(sumber.begin(), 3, ...)): menyalin tepat tiga elemen dari awal sumber. Parameternya jumlah elemen, bukan iterator akhir. - Baris 34-36 (
std::move(pesan.begin(), pesan.end(), tujuan.begin())): algoritmastd::movememindahkan setiap string daripesanketujuandengan move assignment, bukan menyalin karakternya satu per satu. - Baris 38: pada implementasi libstdc++ yang dipakai GCC 14, string yang dipindah menjadi kosong. Standar menjamin objek sumber tetap valid — bisa di-assign ulang atau dihancurkan — tetapi isi pastinya ditentukan implementasi.
back_inserter: Tujuan yang Tumbuh Otomatis #
std::back_inserter(c) adalah helper yang menghasilkan std::back_insert_iterator, iterator output khusus yang selalu menulis di akhir container. Karena kategori output iterator memang hanya mendukung tulis dan maju, back_inserter termasuk kategori paling sederhana di jenjang kategori iterator. Ada juga saudaranya std::front_inserter untuk container yang mendukung push_front seperti std::deque dan std::list.
Algoritma std::move vs Utilitas std::move #
Dua hal bernama std::move ini sering tertukar. Utilitas std::move(x) dari <utility> adalah cast: ia menandai satu ekspresi sebagai rvalue agar move constructor atau move assignment yang dipilih — materi lengkapnya ada di Section Move Semantics dan Value Categories. Sedangkan algoritma std::move(first, last, d_first) dari <algorithm> menerima rentang dan memindahkan setiap elemen ke tujuan. Secara internal, algoritma inilah yang memanggil utilitas std::move per elemen, sehingga elemen bertipe mahal-disalin (misalnya std::string panjang atau std::vector) berpindah dengan hanya menukar pointer internalnya.
Dalam praktik, keluarga ini paling sering dipakai untuk menyaring data ke container lain (copy_if), mengambil sebagian data (copy_n), dan mengalihkan kepemilikan isi antar container (std::move). Untuk mengubah isi saat memindah — bukan sekadar menyalin — alat yang tepat adalah std::transform, topik chapter berikutnya.
Rujukan resmi: std::copy, copy_if, copy_n , algoritma std::move , dan std::back_inserter .