Pustaka standar C++ telah menarik dirinya sendiri selama 15 tahun, dan buktinya terbuka untuk umum
(hftuniversity.com)- Pustaka standar C++ berulang kali secara resmi men-deprecate desain yang keliru atau membiarkannya tetap ada di samping fitur pengganti baru sejak C++11, sehingga pengembang harus mengetahui kapan sebuah lapisan termasuk “lapisan yang sebaiknya tidak digunakan”
- Lapisan penarikan resmi mencakup item seperti
std::auto_ptr, spesifikasi pengecualian dinamis, antarmuka garbage collection C++11, danstd::aligned_storage, yang disertai paper deprecate/penghapusan, sementarastd::functionjuga berada dalam arus penggantian selama 15 tahun yang dikelilingi olehstd::move_only_function,std::copyable_function, danstd::function_ref - Lapisan penghindaran tidak resmi mencakup fitur yang tetap ada di standar tetapi dihindari dalam kode produksi, seperti
std::regexyang lambat,std::asyncyang menunggu di destruktor dan menciptakan jebakan deadlock,<iostream>,std::list,std::deque, danstd::vector<bool> - Masalah kontainer default paling menonjol pada
std::unordered_map,std::map, danstd::list; dalam benchmark workload yang sama, P99 implementasi naif C++ tercatat 302.653 cycles, sedangkan implementasi naif Rust 5.177 cycles, selisih 58 kali - Pilihan stabilitas ABI menjadi perbedaan inti karena, tidak seperti bahasa lain yang mengurangi kesalahan lewat penghapusan, edition, atau transisi versi mayor, C++ pada praktiknya mempertahankan default yang keliru hampir permanen di dalam
std::
Titik awal: penilaian “legacy” untuk std::function
- Tabel referensi cepat
std::copyable_functionmilik Sandor Dargo mengklasifikasikanstd::functionsebagai “Legacy. Avoid in new code.” std::functionmasuk di C++11, sedangkan wrapper pengganti terbarustd::copyable_functionmasuk di C++26; rekomendasi untuk fitur baru ini lebih dekat ke “jangan pakai yang lama” daripada “pakai ini saat Anda butuh callable yang bisa disalin”const operator()padastd::functionmemiliki cacat const-correctness karena dapat memanggil callable non-const, dan hal ini tak bisa diperbaiki tanpa merusak ABI- Sebagai respons atas cacat tersebut,
std::move_only_functionberada pada alur P0288R9 di C++23,std::copyable_functionpada P2548R6 di C++26, danstd::function_refpada P0792R14 di C++26
Fitur standar yang secara resmi ditarik kembali
std::auto_ptrdi-deprecate pada C++11 karena semantik copy-as-move merusak kode generik dan kontainer standar, lalu dihapus pada C++17 lewat N4190; paper yang sama juga menghapus adaptor C++98 di<functional>danstd::random_shufflestd::random_shuffledigantikan olehstd::shufflekarena bergantung padastd::randdan state global- Spesifikasi pengecualian dinamis
throw(X, Y)di-deprecate pada C++11, dihapus pada C++17 lewat P0003R5, dan aliasthrow()dihapus oleh P1152 di C++20 std::iteratordi-deprecate pada C++17 lewat P0174R2, dan penghapusannya di C++26 didorong lewat P3365R1; penggantinya adalah mendefinisikan langsung lima typedefstd::aligned_storagedanstd::aligned_unionmasuk pada C++11 lalu di-deprecate pada C++23 lewat P1413R3; masalah yang disebut mencakup boilerplatetypename ::type,reinterpret_cast, undefined behavior saatLen == 0, dan ketiadaan constexprstd::not1,std::not2,unary_negate, danbinary_negatedi-deprecate pada C++17, dihapus pada C++20, dan digantikan olehstd::not_fndari P0005- Keluarga
std::declare_reachabledari antarmuka garbage collection C++11 dihapus pada C++23 lewat P2186R2 setelah implementasi utama tak pernah menyediakan garbage collector nyata - Concepts TS, Modules TS, Coroutines TS, Reflection TS, Executors TS, dan Networking TS juga mengalami redesain, penggantian, atau penundaan sebelum digabungkan; Reflection beralih ke P2996, dan Executors berubah ke arus sender/receiver P2300
Fitur yang tetap ada di standar tetapi dihindari di lapangan
std::regexmasuk pada C++11, tetapi P1844R1 mencatat di komite bahwa performanya “sangat buruk dibanding solusi lain yang tersedia”; arus penggantinya mencakup CTRE dan P1433R0, sementara di luar standar ada Boost.Regex, RE2, dan PCRE2std::asyncmemblokir di destruktor future yang dikembalikan hingga tugas asinkron selesai, dan N3679 mencatat jebakan deadlock yang ditimbulkannya<iostream>lambat, terikat pada locale, tidak thread-safe untuk formatting, dan terkenal dengan pesan error yang buruk; namun bahkan setelah hadirnyastd::formatdari P0645 di C++20 sertastd::printdanstd::printlndari P2093 di C++23, ia belum di-deprecatestd::listadalah salah satu item yang ditunjukkan Bjarne Stroustrup dalam keynote GoingNative 2012 bahkan untuk workload penyisipan di tengahstd::vectortetap menang, dan jawaban dalam tulisan lanjutan Are lists evil? juga mendekati “ya”std::dequetercatat dalam isu publik Microsoft STL microsoft/STL#147 sebagai item dengan ukuran blok yang diwajibkan standar terlalu kecil dan memerlukan perombakan performa besar pada ABI break berikutnyastd::valarraymasuk pada 1998 sebagai kontainer numerik, tetapi optimisasi expression template tak pernah terwujud, dan menurut cppreference implementasi yang ada tampaknya tidak memiliki kode khusus di luar kontainer umumstd::vector<bool>dibahas secara representatif dalam Onvector<bool>oleh Howard Hinnant; penyimpanan bit-packed itu sendiri berguna, tetapi penamaannya sebagai spesialisasistd::vectormenciptakan jebakan yang memicu perilaku salah dalam kode generik saatT = boolvolatiledi-deprecate pada operasi majemuk serta posisi parameter/pengembalian oleh P1152R4 di C++20, lalu sebagian dibatalkan kembali oleh P2327R1 di C++23, dan pembatalan tambahan dijadwalkan lewat P2866R0 di C++26
Kontainer default yang tak bisa diperbaiki lewat ABI
std::unordered_mappraktis melarang open addressing karena spesifikasi bucket dan stabilitas iterator pada C++11, dan struktur Google SwissTable disebut menunjukkan keunggulan performa sekitar 3 kali dibandingstd::unordered_map- Folly F14, Boost
unordered_flat_map, danankerl::unordered_densejuga merupakan alternatif ke arah yang sama, sementara RustHashMapmemakai port SwissTablehashbrownsebagai default pustaka standarnya std::mapdanstd::setberbasis red-black tree bertipe node, sehingga memerlukan alokasi heap per node dan pointer chasing di setiap iterasi; Abseilbtree_mapdan RustBTreeMapmenghindari masalah yang sama dengan basis B-tree- C++23 menambahkan
std::flat_mapdanstd::flat_setlewat P0429R9, tetapi tidak bisa mengubah desain dasarstd::unordered_map,std::map, danstd::listitu sendiri - Benchmark multi-symbol order book membandingkan
std::unordered_map+std::map+std::listdi C++ denganHashMap+BTreeMap+VecDequedi Rust pada workload yang sama, seed yang sama, dan core terisolasi yang sama
| Implementasi | P99 cycles |
|---|---|
C++ naive (unordered_map + map + list) |
302,653 |
C++ step 1 (flat_hash_map + map + deque) |
9,951 |
C++ step 2 (flat_hash_map + btree_map + deque) |
9,114 |
C++ step 3 (flat_hash_map + btree_map + vector) |
4,268 |
Rust naive (HashMap + BTreeMap + VecDeque) |
5,177 |
- Hanya dengan mengganti
std::listkestd::vectordidapat sekitar 70 kali peningkatan, menggantistd::unordered_mapkeflat_hash_mapmemberi 3–5 kali, dan menggantistd::mapkebtree_mapmemberi 1,09 kali yang masih dalam kisaran noise - Fokus perbandingan ini bukan bahwa bahasa Rust sendiri 58 kali lebih cepat dari C++, melainkan bahwa pustaka standar Rust memilih default yang benar sementara pustaka standar C++ tak bisa memperbaiki tiga default tersebut karena ABI
Masalah Vasa dan penumpukan fitur
- Dokumen WG21 tahun 2018 P0977R0 “Remember the Vasa!” dari Bjarne Stroustrup memakai metafora tenggelamnya kapal perang Swedia Vasa pada 1628, dan menilai komite memiliki “sekitar 150 koki” tanpa cukup membahas dampak tiap fitur terhadap keseluruhan sistem
std::simddibahas sebagai contoh representatif pola yang sama dalam std::simd Is a Solution to the Wrong Problem; fitur ini dimulai oleh Matthias Kretz dari pustaka Vc, lalu melalui P0214, Parallelism TS 2, dan P1928 sebelum masuk ke C++26- Saat
std::simdmasuk ke standar, di luar standar sudah ada Google Highway, ISPC, EVE, xsimd, dan SIMDe; auto-vectorizer GCC dan Clang juga telah membaik, sehingga dikemukakan hasil bahwa loop skalar-O3 -march=nativelebih baik daripadastd::simd std::simdmemiliki masalah seperti waktu kompilasi 10 kali lebih lambat dibanding kode skalar setara, lebih lambat dari auto-vectorizer yang hendak digantikannya, serta tak mampu merepresentasikan vektor scalable-width ARM SVE dan runtime dispatch- Tiga implementasi libstdc++, libc++, dan MSVC STL masing-masing dipelihara oleh tim engineer berukuran satu digit; setiap fitur standar baru menambah matriks pengujian, bug kesesuaian, interaksi antarf fitur, dan item bug tracker yang harus diwarisi perawat berikutnya
std::regexmembawa masalah yang sudah dikenal selama 15 tahun,std::dequememiliki isu yang menuntut redesain, dan modules C++20 digambarkan belum bekerja rapi di ketiga implementasi bahkan 6 tahun setelah distandarkan- Pengetahuan operasional nyata tentang standar C++ modern akhirnya terkonsentrasi pada segelintir spesialis penuh waktu yang memahami kapan lapisan yang salah muncul, workaround pihak ketiga, perbedaan tiga implementasi pustaka standar, dan kesenjangan antara teori dan praktik
Perbedaan dengan bahasa lain: bukan ada tidaknya kesalahan, melainkan tingkat pelestarian
- Python menghapus lebih dari 20 modul pustaka standar lewat PEP 594, menghapus
distutilsdi Python 3.12 lewat PEP 632, dan PEP 387 secara eksplisit memberi wewenang memperpendek siklus deprecate untuk fitur berbahaya atau rusak - Java memproses Applet API lewat jalur 8 tahun: di-deprecate pada Java 9, dijadwalkan dihapus pada Java 17, lalu benar-benar dihapus lewat JEP 504; Nashorn dihapus pada Java 15 lewat JEP 372
- Java SecurityManager menjadi deprecate-for-removal lewat JEP 411 dan dinonaktifkan permanen lewat JEP 486, sementara JEP 398 membahas jalur menuju penghapusan Applet API
- Rust memilih edition 2015, 2018, 2021, dan 2024 per crate lewat opt-in di
Cargo.toml;mem::uninitializeddiganti olehMaybeUninit,std::error::Error::descriptionolehsource, dan makrotry!oleh operator? - C# menerima transisi versi mayor dari .NET Framework ke .NET Core dengan melepas BinaryFormatter, AppDomains, Remoting, Code Access Security, WCF server, dan WebForms
- JavaScript hampir tak pernah menghapus karena kendala kompatibilitas web, tetapi cancelable promises ditarik pada Stage 1, SIMD.js ditinggalkan ke arah WebAssembly SIMD, dan Go memilih hanya meninggalkan
io/ioutildalam status deprecate karena janji kompatibilitas Go 1 - Perbedaan C++ bukan pada apakah ia pernah membuat kesalahan, melainkan pada tingkat pelestariannya terhadap item seperti
std::regex,std::unordered_map,std::vector<bool>,std::valarray, dan cacat const-correctness padastd::function
Pelestarian permanen yang dibentuk stabilitas ABI
- P1863R1 “ABI - Now or Never” adalah arus yang mempertanyakan apakah C++23 harus menerima ABI break atau memilih stabilitas ABI permanen, dan komite pada praktiknya memilih stabilitas ABI permanen
- Pilihan ini membuat perbaikan
std::regex, perpindahanstd::unordered_mapke open addressing, serta perubahan strukturstd::list,std::map, danstd::dequemenjadi sulit - ABI pustaka standar C++ dipaksakan oleh dynamic linker; objek yang dikompilasi dengan satu rilis libstdc++ harus bisa ditautkan dengan objek dari rilis lain, sehingga detail seperti layout
std::stringdan konfigurasistd::regex_traitsterkunci ke binary yang didistribusikan - Kendala ini dirinci dalam dokumen seperti kebijakan ABI libstdc++ dan Itanium C++ ABI
- Pengguna Python bisa memilih
python==3.12, pengguna Rust memilih edition diCargo.toml, dan pengguna Java memilih versi JDK, sementara pengguna C# memilih TFM sepertinet6.0ataunet8.0, tetapi C++ tidak memilikiCargo.tomluntukstd:: -std=c++26memilih header dan aturan bahasa yang ingin dipakai, tetapi tidak menyediakanstd::stringyang berbeda ataustd::unordered_mapyang didesain ulang- Karena itu, pustaka standar C++ yang dikirim ke produksi pada 2026 tetap memuat default yang keliru yang diterima komite sejak 1998, dipertahankan oleh desain dan mekanisme pemaksaan
- Codebase C++ modern di tier-one trading firm, search engine, dan browser sangat bergantung pada pustaka non-standar seperti Boost, Abseil, Folly, EASTL, Chromium
//base, kontainer buatan sendiri, custom allocator, CTRE, Outcome, dan pustaka coroutine
2 komentar
Tulisan aslinya sangat panjang, tetapi setelah dibaca sampai selesai memang terasa cukup kuat nuansa pemuja Rust-nya.
Meski begitu, saya juga jadi tahu banyak informasi yang sebelumnya tidak saya ketahui. Terima kasih untuk tulisan yang bagus ini.
Pendapat di Lobste.rs
Kalau dipikir-pikir apakah ekosistem Rust juga mengalami churn serupa, sepertinya yang besar hanya beberapa
Saat Leakpocalypse, disimpulkan bahwa destructor
Droptidak bisa selalu diandalkan akan dijalankan demi menjaga invarians keamanan, dan perubahan API nyatanya hampir tidak ada selain penghapusanstd::thread::scoped. Setelah itu muncul pengganti yang bisa melakukan hal serupa secara soundstd::mem::uninitializedtelah deprecated dan sekarang dianggap unsound. Tipe-tipeRangeyang lama rencananya akan perlahan digantikan oleh tipe baru yang nyaris sama untuk memperbaiki masalah API yang relatif kecil.std::error::Error::descriptiondideprecated karena sebagian besar tipe error tidak ingin menyimpan string, dan ada pengganti langsung berupa implementasiDisplayMengingat
stdsudah stabil selama 11 tahun, ini cukup mengejutkan; bagianstdlainnya masih ada dan tetap berfungsi, dan 98% darinya masih dianggap Rust idiomatis. Sebaliknya, standard library C++ tampak terlalu ringan tangan dalam menambah fitur, tetapi berada di posisi berbahaya karena sangat konservatif dalam melakukan deprecate apa pun kondisinyaIteratoryang meminjam isinya sendiri. Ini masalah kronis yang terus muncul dalam diskusi Rust sebagai “kenapa ini tidak bisa dipakai dan butuh jalan memutar”Demikian juga
f32danf64yang tidak mengimplementasikanCmpdan malah menyediakan metodef32::total_cmp, juga jadi bagian menyebalkan yang sering bikin engineer baru tersandung, sehingga kita harus menghela napas lalu menjelaskan latar belakangnyamekanisme formatting panic juga kurang bagus, dan ada banyak tulisan blog yang mengeluhkan bahwa panic handler bawaan memakai formatting dan sulit dimatikan, sehingga cukup memakan ukuran executable
Menurut saya pribadi, desain standard library yang usang sangat mengurangi popularitas dan kegunaan C++
Banyak masalah yang sering disalahkan ke bahasanya sendiri sebenarnya lebih tepat diarahkan ke standard library
Misalnya, pernyataan “C++ lambat dikompilasi” itu tidak tepat. Memakai fitur C++ tidak secara inheren lambat; yang membuatnya lambat adalah standard library yang membengkak dengan header dan dependensi dalam jumlah besar, serta terlalu banyak memakai template bahkan untuk abstraksi sederhana
Pernyataan “C++ tidak aman” juga sebagian benar, tetapi desain standard library memperparahnya. Tidak ada alasan pola yang lebih aman dari desain API Rust tidak bisa diterapkan ke standard library baru. Tentu saja, salah satu kelebihan besar C++ adalah kompatibilitas ke belakang, jadi ini masalah yang sangat kompleks
vec[idx]melempar exception atau abort alih-alih menghasilkan akses di luar batas/perilaku tak terdefinisi. Tetapi karena perbedaan bahasa, dalam banyak kasus API aman jauh lebih sulit dibuat di C++Rust pada dasarnya punya destructive move, sedangkan C++ tidak. Karena itu API smart pointer mau tak mau jadi unsafe, atau setidaknya mengejutkan dan mudah bikin crash. Misalnya dengan membuat program abort saat smart pointer yang sudah dipindahkan diakses
Rust punya anotasi lifetime, C++ tidak. Karena itu Rust bisa mencegah hal-hal seperti invalidasi iterator dalam desain API iterator, sedangkan di C++ itu pada praktiknya sangat sulit. Rust juga punya pattern matching, jadi API seperti
Optionbisa memberi pendekatan “cek lalu langsung pakai” dengan ergonomis. C++ juga bisa menyediakan versistd::optionyang tidak menghasilkan UB saat nilai kosong diakses, tetapi penggunaannya akan jauh lebih merepotkan dibanding C++ sekarang maupun Rust. Operator?di Rust juga sangat membantu di siniSaya tahu C++ bisa menambahkan sesuatu yang mirip pattern matching lewat overload set seperti
std::variant, tetapi menurut saya itu jauh lebih sulit dipakai dan lebih mudah bikin salahHanya dengan library modern yang punya string dan array library, beberapa generic container, serta dukungan native untuk allocator, C bisa jadi jauh lebih ergonomis dan lebih mudah dipakai. Tentu beberapa cacat bahasa tidak akan hilang hanya dengan mengganti library, tetapi tetap bisa membawa banyak perbaikan
Kalau melihat codebase C modern, banyak yang luas memakai library kustom untuk allocator, string, vector, hash table, dan operasi file system, dan kalau punya pengalaman dengan C atau pengelolaan resource manual, mengikuti pola itu tidak terlalu sulit
slice<T, N>yang bisa merepresentasikan “pointer ke tepat N byte” atau “pointer ke sejumlah byte berapa pun”Ada
head(n),tail(n),slice(start, end), dan operator indeks, dan semuanya melakukan pemeriksaan batasBekerja dengan abstraksi seperti ini benar-benar menyenangkan, tetapi untuk mendapatkan bahasa yang modern dan agak aman, pada dasarnya kita harus mem-port standard library Rust dan Zig ke C++. Meski begitu, hasilnya tetap sepadan dengan usaha yang dikeluarkan
Kalau mau menulis tulisan seperti ini, tolong tulis sendiri. Mungkin daftarnya memang dibuat sendiri, tetapi memasukkannya ke LLM lalu menayangkan hasilnya di halaman web untuk dibaca orang terasa sangat tidak sopan. Kalau saya melihat satu kali lagi kalimat seperti engineer “yang benar-benar bekerja” diajari menghindari “fitur X” sejak “hari pertama”, rasanya saya bisa gila
Yang memalukan adalah sebenarnya ada banyak hal yang bisa dibahas di sini, tetapi justru tidak mengatakan apa-apa. Pasti ada alasan tulisan ini dibuat, jadi sampaikan alasannya. Pasti ada bagian dari C++ yang membuat penulis kesal, dan ada fitur yang membingungkan. Fitur-fitur ini buruk bukan hanya karena kegagalan desain yang objektif, tetapi juga karena dampaknya pada kita
Pernahkah Anda memakai
std::iteratorlalu dihajar di Slack, atau tidak memakai cast karenareinterpret_castpanjangnya 16 karakter dan sedikit merusak format baris? Akan lebih bagus kalau cerita seperti itu yang muncul di Lobsters. Kalau memang tidak ada cerita seperti itu, jangan dipaksakan, dan jangan suruh GPU menghasilkan kalimat yang sama 10 kali lewat perkalian matriks. Cukup beri anotasi pada bagian yang perlu dikomentari, sisanya tulis sebagai tabel dan bullet pointSaya sudah memakai C++ selama 20 tahun dan masih memakainya sekarang, tapi saya sangat setuju dengan banyak hal di tulisan ini. Hal yang benar-benar bagus saat memakai Rust belakangan ini, lebih dari sekadar keamanan memori, adalah pustaka standar dan ekosistem paket yang luar biasa
Contoh yang mewakili adalah pustaka ranges. Sudah 6 tahun sejak distandardisasi, tetapi pustaka standar utama masih belum mengimplementasikannya secara penuh, dan bahkan ketika ada implementasi pun, combiner yang tersedia hanya sedikit. Padanan Rust-nya, metode
Iterator, berjumlah 76, dan dengan sekalicargo add, traititertoolsmenambahkan 130 lagiHal lain yang sangat saya rindukan adalah pattern matching. Ini bisa membuat tipe union seperti
std::variantmenjadi lebih ergonomis. Usulannya memang sedang dibahas, tetapi bahkan di C++26 pun belum masuk, dan itu disayangkan. Sebaliknya, contracts dan executors justru masuk, padahal sejujurnya saya belum pernah melihat orang di sekitar saya yang memintanyaSecara umum, kriteria yang saya pakai seperti ini. Jika sebuah fitur mendukung use case yang diinginkan dan tidak bisa diekspresikan sebagai pustaka standar, maka itu harus masuk ke bahasa. Kalau memungkinkan, fitur yang diinginkan itu sebaiknya dipecah menjadi elemen-elemen mandiri seminimal mungkin yang juga bisa dipakai untuk tujuan lain
Fitur yang dipakai di hampir semua codebase harus masuk ke pustaka standar. Jika suatu tipe umum dipakai sebagai antarmuka antar pustaka, maka itu juga harus masuk ke pustaka standar. Kita tidak ingin setiap pustaka mendefinisikan tipe tuple atau string versinya sendiri. Dalam C++, yang pertama pada praktiknya memang seperti itu sampai sebelum C++11, dan yang kedua masih begitu karena
std::stringadalah disaster. Ini juga berlaku untuk tipe antarmuka, dan C++ belakangan ini kebanyakan menanganinya dengan conceptsSisanya harus masuk ke pustaka modular yang dapat digunakan ulang. Rust cukup bagus dalam menyediakan kumpulan pustaka eksternal yang stabil dan blessed, jadi tekanan untuk berkata, “semua game yang ditulis dengan Rust butuh struktur data ini, jadi mari masukkan ke pustaka standar” jauh lebih kecil. Orang yang menulis game tinggal mengambil crate yang mereka butuhkan. C++ tidak pernah benar-benar menerima gagasan “paket yang bagus untuk direkomendasikan bagi masalah yang dimiliki banyak orang, meski bukan mayoritas”
Yang mengkhawatirkan adalah hal-hal yang sedang ditambahkan sekarang mana yang pada akhirnya nanti akan ditarik kembali. Contracts baru saja masuk ke C++26, tetapi cacat desain yang serius sudah mulai ditunjukkan
Saya tidak ingin mencela “desain oleh komite” secara umum. Menurut saya, organisasi seperti ini punya tujuan penting dan kekuatan uniknya sendiri. Hanya saja, kekuatan itu bukan pada merancang fitur yang sepenuhnya baru dari green field
Titik di mana WG21 dan WG14 benar-benar bersinar adalah ketika ruang desainnya sudah cukup dieksplorasi dan, kalau bisa, ada beberapa implementasi yang sudah ada, lalu mereka mengangkatnya menjadi fitur standar yang bisa diterima sebagian besar pengguna dan implementator.
std::embedadalah contoh seperti ituSebaliknya, jika sesuatu seperti ekstensi GC yang disebut di artikel,
std::memory_order_consume, atau C++20 modules distandardisasi sebelum ada yang benar-benar berhasil mengimplementasikannya dengan baik, hasilnya cenderung jadi sangat burukDulu saya cukup terkejut saat sadar bahwa C++ tidak melakukan versioning pada standard library-nya. Saya tidak menyangka tulisan ini akan menyoroti hal itu secara tepat
Menarik juga bahwa disebutkan Go bersikap konservatif dengan cara yang mirip soal forward compatibility. Namun Go juga tampaknya sama konservatifnya dalam menambahkan fitur, sehingga berhasil menghindari sebagian besar masalah C++. Ketiadaan ABI yang stabil mungkin juga membantu
Dari library populer yang saya tahu, hanya libcamera yang secara eksplisit mengekspos ABI C++, dan itu cukup merepotkan. Dari pengalaman saya, library C++ pun biasanya mengekspor simbol lewat ABI C, dan itu juga mempermudah interoperabilitas dengan bahasa lain. Mungkin saya yang ketinggalan perkembangan
Dan bukankah ada beberapa quirks dalam kompatibilitas ABI antara Clang dan MSVC? Saya ingat Conan secara eksplisit tidak menganjurkan atau melarang mencampur compiler, jadi saya jadi bertanya-tanya kenapa komite C++ begitu berusaha mempertahankan stabilitas ABI
Ada dua hal yang sangat terkait di sini: spesifikasi standard library dan implementasinya. Spesifikasinya berlaku untuk kombinasi bahasa+library yang utuh, sedangkan implementasi biasanya berusaha mendukung setidaknya satu atau lebih versi spesifikasi
Ada banyak library yang mengekspos antarmuka C++, termasuk yang sangat besar seperti Qt
Masalahnya adalah mesin abstrak C++ tidak mendefinisikan proses linking. Karena itu, ia tidak bisa mendefinisikan bagaimana dynamic library bekerja. Dynamic linking C++ di sistem UNIX mengikuti model C. Pura-pura seolah itu dynamic linking lalu melempar urusannya ke masalah loader. Akibatnya muncullah hal-hal mengerikan seperti copy relocation. Windows punya konsep yang jauh lebih berprinsip tentang apa itu shared library, tetapi karena itu beberapa idiom library C++ di UNIX tidak bisa berjalan di Windows
Shared library menimbulkan masalah besar untuk fitur seperti template C++. Agar template bisa di-instantiate dengan tipe buatan pengguna, definisi penuhnya harus ada di header karena compiler tidak bisa melihat batas antar compilation unit. Dalam shared library, kode yang sama di-instantiate di banyak tempat. Jika program dan library sama-sama meng-instantiate template yang sama dengan parameter yang sama, keduanya akan memiliki salinan, dan linker serta loader harus memastikan hanya satu yang dipakai dalam program akhir yang dimuat
Jika dibandingkan dengan Swift, Swift secara eksplisit menyatakan bahwa “shared library itu ada, dan bahasa mengekspos struktur tingkat bahasa untuk merepresentasikannya”. Jika ingin mengekspos generic melintasi batas shared library, itu bisa, tetapi akan diturunkan menjadi versi dynamic dispatch untuk semua pemanggil eksternal. Di C++ ini juga bisa dibuat manual. Anda bisa membuat template versi umum yang memakai type-erasure wrapper, lalu secara eksplisit menggunakan instantiation konkret lain. Namun ini sulit dan serba manual. Di Swift, ini cukup menjadi “beginilah perilakunya di batas shared library”
Penyembunyian tipe juga serupa. C++ memakai pola
pImpluntuk membuat public interface yang mengekspos perilaku melewati batas library tetapi menyembunyikan implementasinya. Swift memiliki mesin abstrak yang tahu di mana batas library berada, dan mengatakan bahwa “ukuran tipe yang tidak dinyatakan ABI-stable bukan konstanta waktu kompilasi di seberang batas shared library”Standar juga menyangkal realitas dalam bentuk lain. Hampir setiap codebase C++ non-trivial yang pernah saya kerjakan dikompilasi dengan
-fno-rtti -fno-exceptionsatau opsi setara di CL.EXE. Standar tidak mengakui ini sebagai kemungkinan. Sebagian besar fungsi standard library masih mengandalkan exception untuk pelaporan error, jadi bila dikompilasi dengan-fno-exceptionia hanya akan memanggilabort. Ini membuat elemen standard library yang melakukan alokasi memori dinamis tidak bisa dipakai di embedded.std::vector<T>::push_backbisa membuat program crashBagian artikel yang mengatakan “komite bukan hanya gagal menghapus fitur buruk, tetapi juga terus menambahkan fitur baru yang tidak diminta engineer praktis” 100% sama dengan cara contracts muncul. Verus menunjukkan apa yang dimungkinkan oleh sistem contracts yang bagus dalam bahasa yang menargetkan lingkungan seperti C++. Contracts P2900 adalah gabungan kebutuhan yang saling bertentangan, sehingga memperburuk semua masalah yang mungkin cocok ditangani contracts
Saya rasa kesimpulan bahwa “engineer C++” dibayar jauh lebih tinggi daripada “engineer yang bisa memrogram” itu tidak benar. Kenyataannya, tidak ada yang benar-benar menulis kode sesuai standar C++ apa adanya; semua orang menulis sesuai subset-of-a-superset internal favorit masing-masing
go vetjuga bernilai di sini. Itu menyediakan upgrade otomatis untuk perbaikan APISejak tahun lalu saya hampir meninggalkan C++ sepenuhnya, awalnya pindah ke Kotlin lalu ke Swift. Di kantor saya masih harus memelihara C++, tetapi kode baru yang saya tulis jauh lebih rapi, ringkas, dan aman. Ada tradeoff pada ukuran kode dan mungkin performa, tetapi menurut saya itu sepadan
Saya ingat semantik for loop di Go pernah berubah dengan cara yang mematahkan kompatibilitas ke belakang, jadi saya sempat mengira kalimat ini salah: https://go.dev/blog/loopvar-preview
Namun ternyata Go juga memakai pendekatan yang mirip dengan editions di Rust. Semantik hanya berubah jika Anda menyatakan versi Go 1.22 atau lebih baru. Mungkin
io/ioutiljuga bisa dihapus dengan cara seperti ini, tetapi tampaknya tidak cukup layak untuk sampai merusak kode lintas batas editionJika C++ tidak benar-benar mencoba ide-ide buruk ini dan membuktikan bahwa itu memang ide buruk, mungkin Rust tidak akan ada dalam bentuknya yang sekarang. Big Thank You!
Saya tertarik pada pengganti standard library bergaya Rust untuk C++. Saya tahu rpp yang memang mengejar tujuan itu: https://github.com/TheNumbat/rpp
Apakah ada opsi lain? Maksud saya bukan implementasi lain dari stdlib C++ seperti EASTL, melainkan library yang lebih dekat mengikuti Rust. Saya paham beberapa hal seperti
std::initializer_listsudah tertanam di sintaks, tetapi selebihnya semuanya bisa diganti