Topik ini membahas std::string_view dan std::span, dua view non-owning yang menampilkan data milik objek lain tanpa menyalinnya. string_view berlaku pada deret karakter sejak C++17, sedangkan span berlaku pada deret elemen sejak C++20. Setelah membacanya, parameter fungsi dapat ditulis sebagai antarmuka zero-copy dan risiko view yang menggantung dapat dikenali sejak awal.
View non-owning dan biaya penyalinan#
Parameter std::string yang diterima dengan nilai akan menyalin seluruh isinya, lengkap dengan kemungkinan alokasi memori baru. Referensi const std::string& menghindari salinan itu, tetapi tetap memaksa pembentukan string sementara ketika pemanggilnya meneruskan literal. std::string_view memotong persoalan tersebut dengan menyimpan sepasang penunjuk dan panjang saja, sehingga meneruskannya semurah menyalin dua angka dan tidak ada data yang diduplikasi. Sifat tidak memiliki data ini disebut non-owning, dan antarmuka yang tidak menyalin data sama sekali disebut zero-copy.
#include <iostream>
#include <string>
#include <string_view>
void sapa(std::string_view nama) {
std::cout << "Selamat datang, " << nama << "!\n";
}
int main() {
std::string terdaftar = "Citra";
std::string_view terdaftarView = terdaftar;
sapa(terdaftarView); // view atas string yang ada, tanpa penyalinan
sapa("Budi"); // literal: tanpa alokasi string sama sekali
sapa(terdaftarView.substr(0, 2)); // potongan: tetap tanpa penyalinan data
return 0;
}Selamat datang, Citra!
Selamat datang, Budi!
Selamat datang, Ci!Ketiga pemanggilan sapa() di atas menyalin nol karakter. View atas terdaftar hanya meminjam datanya, literal "Budi" langsung dipinjam dari memori konstannya, dan substr(0, 2) pada string_view menghasilkan potongan yang tetap menunjuk data aslinya. Bandingkan dengan std::string::substr yang selalu membuat string baru untuk hasilnya.
Konversi arah sebaliknya tidak otomatis. Membangun std::string dari string_view memerlukan penulisan eksplisit std::string(view), karena operasi itu berpotensi mengalokasi memori dan compiler menolak konversi diam-diam yang mahal. Pola ini menjaga pembaca kode tetap sadar kapan data mulai disalin menjadi milik sendiri.
Operasi string_view#
std::string_view menyediakan pembacaan yang mirip std::string, seperti size(), find(), perbandingan, dan substr(). Perbedaannya terletak pada biaya dan sifatnya, karena substr() pada view hanya menggeser penunjuk dan panjang, sedangkan remove_prefix() dan remove_suffix() menyempitkan jendela tanpa memindahkan satu karakter pun. Semua operasinya bersifat baca, karena view tidak memiliki data yang dilihatnya.
Dua peringatan teknis perlu diingat saat memakai operasi tersebut. data() tidak dijamin berakhir null, sehingga view tidak boleh diteruskan mentah ke fungsi C yang mengandalkan penanda akhir string. Hasil find() dan rfind() juga perlu diuji terhadap npos sebelum dipakai sebagai posisi potongan, karena substr atas npos melempar exception std::out_of_range.
#include <iostream>
#include <string_view>
int main() {
std::string_view baris = "log/suhu/ruang-utama.csv";
std::string_view direktori = baris.substr(0, baris.find('/'));
std::string_view ekstensi = baris.substr(baris.rfind('.'));
std::cout << "Direktori: " << direktori << '\n';
std::cout << "Ekstensi : " << ekstensi << '\n';
std::cout << "Panjang : " << baris.size() << '\n';
baris.remove_prefix(4);
std::cout << "Setelah remove_prefix: " << baris << '\n';
return 0;
}Direktori: log
Ekstensi : .csv
Panjang : 24
Setelah remove_prefix: suhu/ruang-utama.csvPemotongan nama berkas pada contoh tidak melakukan satu alokasi pun, karena seluruh hasilnya adalah jendela atas teks asli yang sama. Pola ini berguna untuk parsing, yaitu memilah masukan teks menjadi bagian-bagian yang diperiksa lalu dibuang tanpa biaya penyalinan.
Risiko view yang menggantung#
View tidak memperpanjang masa hidup data yang dilihatnya. Bila data dasarnya dihancurkan, view yang tersisa hanya menyimpan penunjuk ke memori yang sudah tidak sah, dan kondisi ini disebut view menggantung (dangling view). Kasusnya paling sering muncul pada string sementara, misalnya hasil std::string yang dibuat dalam satu pernyataan atau string lokal yang dikembalikan sebagai view.
// tidak-dijalankan (undefined behavior: data yang dilihat sudah mati)
std::string_view buatSalam() {
std::string salam = "halo";
return salam; // view dibuat atas string lokal yang segera dihancurkan
}Kaidah praktisnya sederhana. Gunakan view sebagai parameter dan sebagai nilai kembalian yang hanya hidup selama ekspresi pemanggilan, dan gunakan std::string sebagai penyimpanan jangka panjang atau anggota kelas. Dengan pembagian itu, salinan tidak terjadi pada jalur data, sedangkan kepemilikannya tetap jelas. Aturan yang sama berlaku di dalam satu fungsi, karena view atas hasil std::string::substr yang sementara juga gugur di akhir pernyataannya.
span: view atas deret elemen (C++20)#
std::span memperluas gagasan yang sama ke deret elemen yang tersambung dalam memori, seperti vector, std::array, dan array mentah. Sebuah span menyimpan penunjuk ke elemen pertama beserta jumlahnya, sehingga fungsi dapat menerima deret apa pun tanpa overloading berlapis dan tanpa perlu mengetahui pemilik datanya. Ukurannya dinamis secara default, yaitu baru diketahui saat program berjalan, sedangkan bentuk berukuran tetap seperti std::span<int, 4> menahan jumlahnya pada tipenya. Bentuk std::span<const int> menyatakan akses baca saja, sejajar dengan string_view pada teks.
// tidak-dijalankan (butuh -std=c++23; perintah build ada di bawah)
#include <iostream>
#include <span>
#include <vector>
double rerata(std::span<const int> data) {
double total = 0;
for (int n : data) {
total += n;
}
return data.empty() ? 0 : total / data.size();
}
int main() {
std::vector<int> suhu = {28, 31, 27, 30};
int mentah[3] = {22, 25, 24};
std::cout << "Rerata vector : " << rerata(suhu) << '\n';
std::cout << "Rerata array : " << rerata(mentah) << '\n';
std::cout << "Elemen pertama: " << suhu[0] << '\n';
return 0;
}# kompilasi dengan standar C++23
g++ -std=c++23 main.cpp -o main
# jalankan program
./mainRerata vector : 29
Rerata array : 23.6667
Elemen pertama: 28Fungsi rerata() yang sama menerima vector dan array mentah tanpa satu pun overloading, karena keduanya dikonversi menjadi std::span<const int> secara otomatis. Span juga menyediakan subspan(), first(), dan last() untuk memotong jendela pengamatan tanpa menyalin elemen, sejajar dengan substr pada string_view. Ukurannya dibaca lewat size() dan keadaan kosongnya diuji lewat empty(). Span tidak memiliki elemen yang dilihatnya, sehingga aturan masa hidup pada bagian sebelumnya berlaku sama persis, yaitu pastikan container sumber tetap hidup selama span dipakai.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul pada pemakaian view.
- Menyimpan view atas string sementara, misalnya
std::string_view nama = std::string("Citra");. Stringnya mati di akhir pernyataan, sedangkan viewnya tersisa. Simpanstd::string-nya atau pastikan sumbernya hidup lebih lama. - Meneruskan
data()string_view ke fungsi C yang menuntut string null-terminated. View tidak dijamin berakhir null, sehingga pembacaannya bisa melewati batas data. Konversi dulu menjadistd::stringbila antarmukanya menuntut penanda akhir. - Memakai
std::spantanpa-std=c++23pada sandbox ini. Header<span>tidak ada pada standar default, sehingga kompilasi gagal dengan pesan errorspan is not a member of std. Tambahkan opsi standarnya pada perintah build.
Ringkasan#
string_viewdanspanadalah view non-owning yang menyimpan penunjuk dan panjang, sehingga antarmuka fungsi menjadi zero-copy atas teks maupun deret elemen.- Operasi view seperti
substr,remove_prefix, danremove_suffixhanya menggeser jendela tanpa alokasi, tetapi seluruhnya bersifat baca. - View tidak memperpanjang masa hidup data yang dilihatnya, sehingga string sementara dan variabel lokal yang dipinjam menghasilkan view menggantung.
Referensi#
- std::basic_string_view — cppreference.com — Operasi string_view dan catatan masa hidupnya.
- std::span — cppreference.com — Definisi span sebagai view non-owning atas deret yang tersambung.
- Introduction to std::string_view — LearnCpp — Pengantar string_view dan parameter string yang efisien.