
Saya ingin menulis posting ini kembali pada bulan Juli, tetapi saya tidak bisa, oh ironi , memutuskan apa namanya. Istilah-istilah yang sukses muncul di benak saya hanya setelah pembicaraan Kate Gregory di CppCon , dan sekarang saya akhirnya dapat memberi tahu Anda cara memanggil fungsi.
Tentu saja, ada nama yang sama sekali tidak membawa informasi, seperti int f(int x)
. Mereka juga tidak perlu digunakan, tetapi ini bukan tentang mereka. Kadang-kadang terjadi bahwa tampaknya informasi dalam judul sudah penuh, tetapi sama sekali tidak ada manfaatnya.
Contoh 1: std :: log2p1 ()
Dalam C ++ 20, beberapa fungsi baru untuk operasi bit ditambahkan ke header, antara lain std::log2p1
. Ini terlihat seperti ini:
int log2p1(int i) { if (i == 0) return 0; else return 1 + int(std::log2(x)); }
Yaitu, untuk setiap bilangan alami, fungsi mengembalikan logaritma binernya ditambah 1, dan untuk 0 mengembalikannya 0. Dan ini bukan masalah sekolah untuk operator if / else, ini benar-benar hal yang berguna - jumlah bit minimum yang sesuai dengan nilai ini. Hanya menebak tentangnya dengan nama fungsinya hampir tidak mungkin.
Contoh 2: std :: bless ()
Sekarang bukan tentang namanya
Penyimpangan kecil: dalam C ++, pointer aritmatika hanya berfungsi dengan pointer ke elemen array. Yang, pada prinsipnya, adalah logis: dalam kasus umum, himpunan objek tetangga tidak diketahui dan "apa pun dapat terjadi dalam sepuluh byte di sebelah kanan variabel i
". Ini adalah perilaku yang jelas-jelas kabur.
int obj = 0; int* ptr = &obj; ++ptr;
Tetapi pembatasan seperti itu menyatakan sejumlah besar perilaku tidak terbatas kode yang ada. Sebagai contoh, berikut ini adalah implementasi sederhana std::vector<T>::reserve()
:
void reserve(std::size_t n) {
Kami telah mengalokasikan memori, memindahkan semua objek, dan sekarang mencoba memastikan bahwa pointer menunjukkan ke mana harus pergi. Berikut ini hanya tiga baris terakhir yang tidak terdefinisi, karena mengandung operasi aritmatika pada pointer di luar array!
Tentu saja, bukan programmer yang harus disalahkan. Masalahnya adalah dengan standar C ++ itu sendiri, yang menyatakan sepotong kode yang jelas masuk akal ini sebagai perilaku yang tidak terdefinisi. Oleh karena itu, P0593 menyarankan untuk memperbaiki standar dengan menambahkan beberapa fungsi (seperti ::operator new
dan std::malloc
) kemampuan untuk membuat array sesuai kebutuhan. Semua pointer yang dibuat oleh mereka secara ajaib akan menjadi pointer ke array, dan operasi aritmatika dapat dilakukan dengannya.
Masih belum tentang nama, tunggu sebentar.
Tetapi kadang-kadang, operasi pada pointer diperlukan ketika bekerja dengan memori yang salah satu fungsi ini tidak mengalokasikan. Sebagai contoh, fungsi deallocate()
pada dasarnya bekerja dengan memori mati, di mana tidak ada objek sama sekali, tetapi masih harus menjumlahkan pointer dan ukuran area. Untuk kasus ini, P0593 menawarkan fungsi std::bless(void* ptr, std::size_t n)
(ada fungsi lain di sana, yang juga disebut bless
, tetapi ini bukan tentang itu). Ini tidak berpengaruh pada komputer fisik kehidupan nyata, tetapi menciptakan objek untuk mesin abstrak yang akan memungkinkan penggunaan aritmatika pointer.
Nama std::bless
bersifat sementara.
Jadi namanya.
Di Cologne, LEWG diberi tugas untuk membuat nama untuk fungsi ini. Opsi implicitly_create_objects()
dan implicitly_create_objects_as_needed()
diusulkan, karena ini adalah apa fungsi tidak.
Saya tidak suka opsi ini.
Contoh 3: std :: partial_sort_copy ()
Contoh diambil dari presentasi Kate
Ada fungsi std::sort
, yang mengurutkan elemen-elemen dari wadah:
std::vector<int> vec = {3, 1, 5, 4, 2}; std::sort(vec.begin(), vec.end());
Ada juga std::partial_sort
, yang hanya mengurutkan bagian dari elemen:
std::vector<int> vec = {3, 1, 5, 4, 2}; std::partial_sort(vec.begin(), vec.begin() + 3, vec.end());
Dan masih ada std::partial_sort_copy
, yang juga mengurutkan bagian dari elemen, tetapi pada saat yang sama wadah lama tidak berubah, tetapi mentransfer nilainya ke yang baru:
const std::vector<int> vec = {3, 1, 5, 4, 2}; std::vector<int> out; out.resize(3); std::partial_sort_copy(vec.begin(), vec.end(), out.begin(), out.end());
Kate mengklaim bahwa std::partial_sort_copy
adalah nama yang biasa saja, dan saya setuju dengannya.
Nama Implementasi dan Nama Hasil
Tidak satu pun dari nama-nama yang tercantum, secara tegas, salah : mereka semua dengan sempurna menggambarkan apa fungsi tidak. std::log2p1()
benar-benar menghitung logaritma biner dan menambahkannya; implicitly_create_objects()
secara implisit membuat objek, dan std::partial_sort_copy()
sebagian mengurutkan wadah dan menyalin hasilnya. Namun, saya tidak suka semua nama ini, karena mereka tidak berguna .
Tidak ada programmer yang duduk dan berpikir โSaya berharap saya bisa mengambil logaritma biner dan menambahkannyaโ. Dia perlu tahu berapa banyak bit nilai yang diberikan akan cocok, dan dia gagal mencari dermaga untuk sesuatu seperti bit_width
. Pada saat ia mencapai pengguna perpustakaan, apa yang harus dilakukan dengan logaritma biner dengannya, ia sudah menulis implementasinya (dan kemungkinan besar melewatkan cek nol). Bahkan jika std::log2p1
ternyata merupakan keajaiban dalam kode, yang berikutnya untuk melihat kode ini harus kembali memahami apa itu dan mengapa itu diperlukan. bit_width(max_value)
tidak akan memiliki masalah seperti itu.
Demikian pula, tidak ada yang perlu "secara implisit membuat objek" atau "mengurutkan sebagian salinan vektor" - mereka perlu menggunakan kembali memori atau mendapatkan 5 nilai terbesar dalam urutan menurun. Sesuatu seperti recycle_storage()
(yang juga disarankan sebagai nama std::bless
) dan top_n_sorted()
akan jauh lebih jelas.
Kate menggunakan nama implementasi istilah untuk std::partial_sort_copy()
, tetapi cocok dengan dua fungsi lainnya juga. Implementasi nama mereka benar-benar digambarkan dengan sempurna. Itu hanya pengguna membutuhkan nama hasilnya - apa yang didapatnya dengan memanggil fungsi. Untuk struktur internalnya, dia tidak peduli, dia hanya ingin mengetahui ukuran dalam bit atau menggunakan kembali memori.
Memberi nama fungsi berdasarkan spesifikasinya berarti menciptakan kesalahpahaman antara pengembang perpustakaan dan penggunanya. Anda harus selalu ingat kapan dan bagaimana fungsi akan digunakan.
Kedengarannya klise, ya. Tapi dilihat dari std::log2p1()
, ini jauh dari jelas bagi semua orang. Selain itu, terkadang tidak sesederhana itu.
Contoh 4: std :: popcount ()
std::popcount()
, seperti std::log2p1()
, di C ++ 20 diusulkan untuk ditambahkan ke <bit>
. Dan ini, tentu saja, adalah nama yang sangat buruk. Jika Anda tidak tahu apa fungsi ini, tidak mungkin untuk menebak. Tidak hanya singkatannya yang membingungkan (ada pop di namanya, tetapi pop / push tidak ada hubungannya dengan itu) - menguraikan jumlah populasi (menghitung populasi? Jumlah populasi?) Juga tidak membantu.
Di sisi lain, std::popcount()
ideal untuk fungsi ini karena memanggil instruksi assembly popcount. Ini bukan hanya nama implementasinya - ini adalah deskripsi lengkapnya.
Namun, dalam hal ini, kesenjangan antara pengembang bahasa dan pemrogram tidak terlalu besar. Instruksi yang menghitung jumlah unit dalam kata biner disebut popcount dari tahun enam puluhan. Untuk orang yang tahu apa-apa tentang operasi bit, nama seperti itu sangat jelas.
Ngomong-ngomong, pertanyaan yang bagus: apakah Anda memikirkan nama yang sesuai untuk pemula, atau membuat mereka terbiasa dengan barang lama?
Selamat akhir?
P1956 menyarankan mengubah nama std::log2p1()
menjadi std::bit_width()
. Proposal ini kemungkinan akan diterima di C ++ 20. std::ceil2
dan std::floor2
juga akan diganti namanya menjadi std :: bit_ceil () dan std :: bit_floor (). Nama lama mereka juga tidak terlalu, tetapi karena alasan lain.
LEWG di Cologne tidak memilih implicitly_create_objects[_as_needed]
atau recycle_storage
sebagai nama untuk std::bless
. Mereka memutuskan untuk tidak memasukkan fungsi ini dalam standar sama sekali. Efek yang sama dapat dicapai dengan secara eksplisit membuat array byte, oleh karena itu, kata mereka, fungsi tersebut tidak diperlukan. Saya tidak suka ini karena memanggil std::recycle_storage()
akan lebih mudah dibaca. std::bless()
masih ada, tetapi sekarang disebut start_lifetime_as
. Saya suka itu. Itu harus masuk ke C ++ 23.
Tentu saja, std::partial_sort_copy()
tidak std::partial_sort_copy()
lagi diganti namanya - dengan nama ini ia masuk ke standar kembali pada tahun 1998. Tapi setidaknya std::log2p1
diperbaiki, dan itu tidak buruk.
Dalam menemukan nama fungsi, Anda perlu memikirkan siapa yang akan menggunakannya dan apa yang dia inginkan dari mereka. Seperti kata Kate, penamaan membutuhkan empati .