Apakah COW basic_string
dilarang di C ++ 11 dan yang lebih baru?
Mengenai
” Apakah saya benar bahwa C ++ 11 tidak menerima implementasi berbasis COW std::string
?
Iya.
Mengenai
” Jika demikian, apakah pembatasan ini secara eksplisit dinyatakan di suatu tempat dalam standar baru (di mana)?
Hampir secara langsung, dengan persyaratan kompleksitas konstan untuk sejumlah operasi yang memerlukan O ( n ) penyalinan fisik dari data string dalam implementasi KK.
Misalnya untuk fungsi anggota
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
… Yang dalam implementasi COW akan - keduanya memicu penyalinan data string untuk membatalkan pembagian nilai string, standar C ++ 11 memerlukan
C ++ 11 §21.4.5 / 4 :
” Kompleksitas: konstanta waktu.
… Yang mengesampingkan penyalinan data tersebut, dan karenanya, KK.
C ++ 03 didukung implementasi SAPI oleh tidak memiliki persyaratan kompleksitas konstan ini, dan oleh, di bawah kondisi pembatasan tertentu, yang memungkinkan panggilan ke operator[]()
, at()
, begin()
, rbegin()
, end()
, atau rend()
untuk referensi invalidate, pointer dan iterator mengacu pada item tali, yaitu untuk kemungkinan dikenakan Penyalinan data KK. Dukungan ini telah dihapus di C ++ 11.
Apakah COW juga dilarang melalui aturan pembatalan C ++ 11?
Dalam jawaban lain yang pada saat penulisan dipilih sebagai solusi, dan yang sangat disukai dan oleh karena itu dipercaya, ditegaskan bahwa
” Untuk string COW, memanggil non- const
operator[]
akan memerlukan pembuatan salinan (dan referensi yang tidak valid), yang tidak diizinkan oleh [kutipan] paragraf di atas [C ++ 11 §21.4.1 / 6]. Oleh karena itu, tidak lagi legal untuk memiliki string COW di C ++ 11.
Pernyataan tersebut tidak benar dan menyesatkan dalam dua hal utama:
- Ini salah menunjukkan bahwa hanya pengakses non-
const
item yang perlu memicu penyalinan data COW.
Tetapi pengakses const
item juga perlu memicu penyalinan data, karena mereka mengizinkan kode klien untuk membentuk referensi atau petunjuk yang (dalam C ++ 11) tidak diizinkan untuk membatalkannya nanti melalui operasi yang dapat memicu penyalinan data COW.
- Ini salah mengasumsikan bahwa penyalinan data KK dapat menyebabkan pembatalan referensi.
Tetapi dalam implementasi yang benar, penyalinan data COW, pembatalan pembagian nilai string, dilakukan pada titik sebelum ada referensi yang dapat dibatalkan.
Untuk melihat bagaimana implementasi C ++ 11 COW yang benar basic_string
akan bekerja, ketika persyaratan O (1) yang membuat ini tidak valid diabaikan, pikirkan implementasi di mana string dapat beralih di antara kebijakan kepemilikan. Instance string dimulai dengan kebijakan Sharable. Dengan kebijakan ini aktif, tidak ada referensi item eksternal. Instance dapat bertransisi ke kebijakan Unik, dan harus melakukannya ketika referensi item berpotensi dibuat seperti dengan panggilan ke .c_str()
(setidaknya jika itu menghasilkan pointer ke buffer internal). Dalam kasus umum beberapa contoh berbagi kepemilikan nilai, ini memerlukan penyalinan data string. Setelah transisi ke kebijakan Unik, instance hanya dapat beralih kembali ke Sharable melalui operasi yang membatalkan semua referensi, seperti penetapan.
Jadi, walaupun kesimpulan jawaban itu, bahwa string KK dikesampingkan, benar, alasan yang ditawarkan salah dan sangat menyesatkan.
Saya menduga penyebab kesalahpahaman ini adalah catatan non-normatif dalam lampiran C C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], tentang §21.3:
Ubah : basic_string
persyaratan tidak lagi mengizinkan string yang dihitung referensi
Rasional: Invalidasi sedikit berbeda dengan string yang dihitung referensi. Perubahan ini mengatur perilaku (sic) untuk Standar Internasional ini.
Efek pada fitur asli: Kode C ++ 2003 yang valid dapat dijalankan secara berbeda dalam Standar Internasional ini
Di sini alasan menjelaskan alasan utama mengapa seseorang memutuskan untuk menghapus dukungan COW khusus C ++ 03. Alasan ini, mengapa , bukanlah bagaimana standar secara efektif melarang penerapan KK. Standar melarang sapi melalui persyaratan O (1).
Singkatnya, aturan pembatalan C ++ 11 tidak mengesampingkan implementasi COW std::basic_string
. Tetapi mereka mengesampingkan implementasi COW tak terbatas gaya C ++ 03 yang cukup efisien seperti yang ada di setidaknya salah satu implementasi pustaka standar g ++. Dukungan khusus C ++ 03 COW memungkinkan efisiensi praktis, khususnya menggunakan const
pengakses item, dengan mengorbankan aturan yang halus dan rumit untuk pembatalan:
C ++ 03 §21.3 / 5 yang mencakup dukungan COW "panggilan pertama":
” Referensi, pointer, dan iterators mengacu pada unsur-unsur dari basic_string
urutan mungkin tidak berlaku karena penggunaan berikut yang basic_string
objek:
- Sebagai argumen untuk fungsi non-anggota swap()
(21.3.7.8), operator>>()
(21.3.7.9), dan getline()
(21.3. 7.9).
- Sebagai argumen untuk basic_string::swap()
.
- Memanggil data()
dan c_str()
fungsi anggota.
- Memanggil non const
fungsi anggota, kecuali operator[]()
, at()
, begin()
, rbegin()
, end()
, dan rend()
.
- Setelah ke salah satu penggunaan di atas kecuali bentuk insert()
dan erase()
yang iterator kembali, panggilan pertama untuk non const
fungsi anggota operator[]()
, at()
, begin()
, rbegin()
,end()
, atau rend()
.
Aturan-aturan ini sangat rumit dan halus sehingga saya ragu banyak programmer, jika ada, dapat memberikan ringkasan yang tepat. Saya tidak bisa.
Bagaimana jika persyaratan O (1) diabaikan?
Jika persyaratan waktu konstan C ++ 11 pada mis operator[]
diabaikan, maka COW untuk basic_string
dapat secara teknis layak, tetapi sulit untuk diterapkan.
Operasi yang dapat mengakses konten string tanpa melakukan penyalinan data COW meliputi:
- Penggabungan melalui
+
.
- Output melalui
<<
.
- Menggunakan
basic_string
sebagai argumen untuk fungsi pustaka standar.
Yang terakhir karena pustaka standar diizinkan untuk mengandalkan implementasi pengetahuan dan konstruksi tertentu.
Selain itu, implementasi dapat menawarkan berbagai fungsi non-standar untuk mengakses konten string tanpa memicu penyalinan data COW.
Faktor utama yang menyulitkan adalah bahwa dalam C ++ 11 basic_string
akses item harus memicu penyalinan data (membatalkan pembagian data string) tetapi diperlukan untuk tidak melempar , misalnya C ++ 11 §21.4.5 / 3 “ Throws: Nothing.”. Sehingga tidak dapat menggunakan alokasi dinamis biasa untuk membuat buffer baru untuk penyalinan data COW. Salah satu cara untuk mengatasinya adalah dengan menggunakan heap khusus di mana memori dapat dicadangkan tanpa benar-benar dialokasikan, dan kemudian mencadangkan jumlah yang diperlukan untuk setiap referensi logis ke nilai string. Mencadangkan dan membatalkan reservasi di heap seperti itu dapat menjadi waktu yang konstan, O (1), dan mengalokasikan jumlah yang telah dipesan, dapatnoexcept
. Untuk memenuhi persyaratan standar, dengan pendekatan ini tampaknya perlu ada satu heap berbasis reservasi khusus per pengalokasi yang berbeda.
Catatan:
¹ Pengakses const
item memicu penyalinan data KK karena memungkinkan kode klien untuk mendapatkan referensi atau penunjuk ke data, yang tidak diizinkan untuk dibatalkan oleh penyalinan data selanjutnya yang dipicu oleh misalnya pengakses non- const
item.