Apa aturan tentang menggunakan garis bawah pada pengidentifikasi C ++?

931

Adalah umum di C ++ untuk menamai variabel anggota dengan semacam awalan untuk menunjukkan fakta bahwa mereka adalah variabel anggota, bukan variabel atau parameter lokal. Jika Anda berasal dari latar belakang MFC, Anda mungkin akan menggunakan m_foo. Saya juga melihat myFoosesekali.

C # (atau mungkin hanya .NET) tampaknya merekomendasikan menggunakan hanya garis bawah, seperti pada _foo. Apakah ini diizinkan oleh standar C ++?

Roger Lipscombe
sumber
3
Halaman manual glibc tentang hal itu dapat ditemukan di gnu.org/software/libc/manual/html_node/Reserved-Names.html Edit: lihat juga opengroup.org/onlinepubs/009695399/functions/xsh_chap02_02.html
CesarB
6
Hanya untuk mencatat bahwa ketidaktahuan aturan-aturan ini tidak selalu menyiratkan bahwa kode Anda tidak akan dikompilasi atau dijalankan, tetapi kemungkinan kode Anda tidak akan portabel untuk berbagai kompiler dan versi, karena tidak dapat dijamin bahwa tidak akan ada nama bentrokan. Untuk mendukung ini saya tahu implementasi tertentu dari sistem penting yang telah digunakan sebagai konvensi penamaan _ huruf kapital di mana-mana. Di sana tidak ada kesalahan karena ini. Tentu saja itu praktik yang buruk.
g24l

Jawaban:

852

Aturan (yang tidak berubah di C ++ 11):

  • Dicadangkan dalam ruang lingkup apa pun, termasuk untuk digunakan sebagai makro implementasi :
    • pengidentifikasi dimulai dengan garis bawah diikuti segera oleh huruf besar
    • pengidentifikasi yang berisi garis bawah yang berdekatan (atau "garis bawah ganda")
  • Dicadangkan di namespace global:
    • pengidentifikasi dimulai dengan garis bawah
  • Juga, semua yang ada di stdnamespace dicadangkan. (Anda diizinkan untuk menambahkan spesialisasi templat.)

Dari Standar C ++ 2003:

17.4.3.1.2 Nama global [lib.global.names]

Kumpulan tanda nama dan fungsi tertentu selalu dicadangkan untuk implementasi:

  • Setiap nama yang berisi garis bawah ganda ( __) atau dimulai dengan garis bawah diikuti dengan huruf besar (2.11) dicadangkan untuk implementasi untuk penggunaan apa pun.
  • Setiap nama yang dimulai dengan garis bawah disediakan untuk implementasi untuk digunakan sebagai nama di namespace global. 165

165) Nama-nama tersebut juga dicadangkan di namespace ::std(17.4.3.1).

Karena C ++ didasarkan pada standar C (1.1 / 2, C ++ 03) dan C99 adalah referensi normatif (1.2 / 1, C ++ 03) ini juga berlaku, dari 1999 C Standard:

7.1.3 Pengidentifikasi yang dicadangkan

Setiap header menyatakan atau mendefinisikan semua pengidentifikasi yang tercantum dalam subclause terkait, dan secara opsional mendeklarasikan atau mendefinisikan pengidentifikasi yang tercantum dalam subclause arah pengarahan pustaka terkait dan pengidentifikasi yang selalu dicadangkan baik untuk penggunaan apa pun atau untuk digunakan sebagai pengidentifikasi lingkup file.

  • Semua pengidentifikasi yang dimulai dengan garis bawah dan huruf besar atau garis bawah lainnya selalu dicadangkan untuk penggunaan apa pun.
  • Semua pengidentifikasi yang dimulai dengan garis bawah selalu dicadangkan untuk digunakan sebagai pengidentifikasi dengan cakupan file di ruang biasa dan nama tag.
  • Setiap nama makro di salah satu subclauses berikut (termasuk arah pustaka masa depan) dicadangkan untuk digunakan sebagaimana ditentukan jika salah satu header terkait disertakan; kecuali secara eksplisit dinyatakan sebaliknya (lihat 7.1.4).
  • Semua pengidentifikasi dengan hubungan eksternal di salah satu subclauses berikut (termasuk arah perpustakaan di masa depan) selalu dicadangkan untuk digunakan sebagai pengidentifikasi dengan tautan eksternal. 154
  • Setiap pengidentifikasi dengan cakupan file yang tercantum dalam salah satu subclauses berikut (termasuk arah pustaka masa depan) dicadangkan untuk digunakan sebagai nama makro dan sebagai pengidentifikasi dengan cakupan file dalam ruang nama yang sama jika ada header terkait yang disertakan.

Tidak ada pengidentifikasi lain yang dicadangkan. Jika program mendeklarasikan atau mendefinisikan pengidentifikasi dalam konteks di mana ia dicadangkan (selain dari yang diizinkan oleh 7.1.4), atau mendefinisikan pengidentifikasi yang dicadangkan sebagai nama makro, perilaku tersebut tidak ditentukan.

Jika program menghapus (dengan #undef) definisi makro pengidentifikasi di grup pertama yang tercantum di atas, perilaku tidak terdefinisi.

154) Daftar pengidentifikasi milik dengan linkage eksternal meliputi errno, math_errhandling, setjmp, dan va_end.

Pembatasan lain mungkin berlaku. Sebagai contoh, standar POSIX menyimpan banyak pengidentifikasi yang cenderung muncul dalam kode normal:

  • Nama yang diawali dengan huruf kapital Ediikuti dengan angka atau huruf besar:
    • dapat digunakan untuk nama kode kesalahan tambahan.
  • Nama yang dimulai dengan salah satu isatau todiikuti dengan huruf kecil
    • dapat digunakan untuk pengujian karakter tambahan dan fungsi konversi.
  • Nama yang diawali dengan LC_diikuti oleh huruf besar
    • dapat digunakan untuk makro tambahan yang menetapkan atribut lokal.
  • Nama-nama dari semua fungsi matematika yang ada suffix dengan fatau ldicadangkan
    • untuk fungsi terkait yang beroperasi pada float dan argumen double panjang, masing-masing.
  • Nama-nama yang dimulai dengan SIGdiikuti oleh huruf besar dicadangkan
    • untuk nama sinyal tambahan.
  • Nama-nama yang dimulai dengan SIG_diikuti oleh huruf besar dicadangkan
    • untuk aksi sinyal tambahan.
  • Nama-nama yang dimulai dengan str,, mematau wcsdiikuti dengan huruf kecil dicadangkan
    • untuk fungsi string dan array tambahan.
  • Nama-nama yang dimulai dengan PRIatau SCNdiikuti oleh huruf kecil apa pun atau Xdicadangkan
    • untuk makro penentu format tambahan
  • Nama yang diakhiri dengan _tdicadangkan
    • untuk nama tipe tambahan.

Meskipun menggunakan nama-nama ini untuk tujuan Anda sendiri saat ini mungkin tidak menyebabkan masalah, mereka memang meningkatkan kemungkinan konflik dengan versi masa depan dari standar itu.


Secara pribadi saya tidak memulai pengidentifikasi dengan garis bawah. Tambahan baru untuk aturan saya: Jangan gunakan garis bawah ganda di mana saja, yang mudah karena saya jarang menggunakan garis bawah.

Setelah melakukan penelitian pada artikel ini saya tidak lagi mengakhiri pengidentifikasi saya _t karena ini dicadangkan oleh standar POSIX.

Aturan tentang pengidentifikasi yang diakhiri dengan sangat _tmengejutkan saya. Saya pikir itu adalah standar POSIX (belum yakin) mencari klarifikasi dan bab dan ayat resmi. Ini dari manual libtool GNU , daftar nama yang dipesan.

CesarB menyediakan tautan berikut ke simbol dan catatan milik POSIX 2004 'yang banyak awalan dan sufiks yang dipesan lainnya ... dapat ditemukan di sana'. The POSIX 2008 simbol dilindungi didefinisikan di sini. Pembatasannya agak lebih bernuansa daripada yang di atas.

Roger Pate
sumber
14
Standar C ++ tidak "mengimpor" yang C, bukan? Mereka mengimpor tajuk tertentu, tetapi bukan bahasa secara keseluruhan, atau aturan penamaan, sejauh yang saya tahu. Tapi ya, yang mengejutkan saya juga. Tapi karena itu C, itu hanya bisa berlaku untuk global ns. Harus aman menggunakan _t di dalam kelas saat saya membacanya
jalf
27
Standar C ++ tidak "mengimpor" Standar C. Ini referensi Standar C. Pengenalan C ++ library mengatakan "Library juga menyediakan fasilitas dari Standard C Library". Itu melakukannya dengan memasukkan header dari perpustakaan C Standard dengan perubahan yang sesuai, tetapi tidak dengan "mengimpor" itu. Standar C ++ memiliki seperangkat aturan sendiri yang menjelaskan nama yang dicadangkan. Jika nama yang dicadangkan dalam C harus dicadangkan dalam C ++, itu adalah tempat untuk mengatakan ini. Tetapi Standar C ++ tidak mengatakan demikian. Jadi saya tidak percaya bahwa hal-hal yang dicadangkan dalam C dicadangkan dalam C ++ - tetapi saya bisa saja salah.
Johannes Schaub - litb
8
Inilah yang saya temukan tentang masalah "_t": n1256 (C99 TC3) mengatakan: "Nama typedef dimulai dengan int atau uint dan diakhiri dengan _t" dicadangkan. Saya pikir itu masih memungkinkan menggunakan nama-nama seperti "foo_t" - tetapi saya pikir ini kemudian dicadangkan oleh POSIX.
Johannes Schaub - litb
59
Jadi 'toleransi' dicadangkan oleh POSIX karena dimulai dengan 'to' + huruf kecil? Saya yakin banyak kode melanggar aturan ini!
Sjoerd
23
@LokiAstari, " Standar C ++ didefinisikan dalam istilah standar C. Pada dasarnya dikatakan C ++ adalah C dengan perbedaan dan penambahan ini. " Omong kosong! C ++ hanya mereferensikan standar C di [basic.fundamental] dan pustaka. Jika apa yang Anda katakan itu benar, di mana C ++ mengatakan itu _Booldan _Imaginarytidak ada di C ++? Bahasa C ++ didefinisikan secara eksplisit, bukan dalam hal "edit" ke C, jika tidak standarnya bisa jauh lebih pendek!
Jonathan Wakely
198

Aturan untuk menghindari tabrakan nama keduanya ada dalam standar C ++ (lihat buku Stroustrup) dan disebutkan oleh guru C ++ (Sutter, dll.).

Aturan pribadi

Karena saya tidak ingin menangani kasus, dan menginginkan aturan yang sederhana, saya telah merancang aturan pribadi yang sederhana dan benar:

Saat memberi nama simbol, Anda akan menghindari tabrakan dengan kompiler / OS / pustaka standar jika Anda:

  • jangan pernah memulai simbol dengan garis bawah
  • tidak pernah memberi nama simbol dengan dua garis bawah berturut-turut di dalamnya.

Tentu saja, meletakkan kode Anda di namespace yang unik juga membantu menghindari tabrakan (tetapi tidak akan melindungi dari makro jahat)

Beberapa contoh

(Saya menggunakan makro karena mereka lebih banyak mencemari kode simbol C / C ++, tapi bisa apa saja dari nama variabel ke nama kelas)

#define _WRONG
#define __WRONG_AGAIN
#define RIGHT_
#define WRONG__WRONG
#define RIGHT_RIGHT
#define RIGHT_x_RIGHT

Ekstrak dari konsep C ++ 0x

Dari file n3242.pdf (saya berharap teks standar akhir menjadi serupa):

17.6.3.3.2 Nama global [global.names]

Kumpulan tanda nama dan fungsi tertentu selalu dicadangkan untuk implementasi:

- Setiap nama yang berisi garis bawah ganda _ _ atau dimulai dengan garis bawah diikuti dengan huruf besar (2.12) dicadangkan untuk implementasi untuk penggunaan apa pun.

- Setiap nama yang dimulai dengan garis bawah disediakan untuk implementasi untuk digunakan sebagai nama di namespace global.

Tetapi juga:

17.6.3.3.5 Sufiks literal yang ditentukan pengguna [usrlit.suffix]

Pengidentifikasi suffix literal yang tidak dimulai dengan garis bawah dicadangkan untuk standardisasi di masa mendatang.

Klausa terakhir ini membingungkan, kecuali jika Anda menganggap bahwa nama yang dimulai dengan satu garis bawah dan diikuti oleh huruf kecil akan baik-baik saja jika tidak didefinisikan dalam namespace global ...

paercebal
sumber
9
@ Meysam: __WRONG_AGAIN__berisi dua garis bawah berturut-turut (dua di awal, dan dua di akhir), jadi ini salah menurut standar.
paercebal
8
@ BЈовић: WRONG__WRONGberisi dua garis bawah berturut-turut (dua di tengah), jadi ini salah menurut standar
paercebal
2
menempatkan kode Anda di namespace yang unik juga membantu menghindari tabrakan : tetapi ini masih belum cukup, karena pengidentifikasi dapat bertabrakan dengan kata kunci terlepas dari ruang lingkup (misalnya __attribute__untuk GCC).
Ruslan
1
Mengapa ada masalah memiliki dua garis bawah berturut-turut di tengah sesuai dengan standar? Sufiks literal yang ditentukan pengguna berlaku untuk nilai literal seperti 1234567Latau 4.0f; IIRC ini merujuk pada ohttp: //en.cppreference.com/w/cpp/language/user_literal
Jason S
2
Why is there any problem of having two consecutive underscores in the middle according to the standard?Karena standar mengatakan itu dicadangkan. Ini bukan saran tentang gaya baik atau buruk. Ini keputusan dari standar. Mengapa mereka memutuskan ini? Saya kira kompiler pertama sudah menggunakan konvensi seperti itu secara informal sebelum standardisasi.
paercebal
38

Dari MSDN :

Penggunaan dua karakter garis bawah berurutan (__) di awal pengidentifikasi, atau satu garis bawah utama yang diikuti dengan huruf kapital, dicadangkan untuk implementasi C ++ di semua lingkup. Anda harus menghindari menggunakan satu garis bawah utama diikuti dengan huruf kecil untuk nama-nama dengan cakupan file karena kemungkinan konflik dengan pengidentifikasi cadangan saat ini atau di masa depan.

Ini berarti bahwa Anda dapat menggunakan satu garis bawah sebagai awalan variabel anggota, asalkan diikuti oleh huruf kecil.

Ini tampaknya diambil dari bagian 17.4.3.1.2 dari standar C ++, tetapi saya tidak dapat menemukan sumber asli untuk standar online penuh.

Lihat juga pertanyaan ini .

Roger Lipscombe
sumber
2
Saya menemukan teks serupa di n3092.pdf (konsep standar C ++ 0x) di bagian: "17.6.3.3.2 Nama global"
paercebal
7
Menariknya, ini tampaknya menjadi satu-satunya jawaban yang memiliki jawaban langsung dan ringkas untuk pertanyaan itu.
hyde
9
@ Hyde: Sebenarnya, tidak, karena itu melompati aturan untuk tidak memiliki pengidentifikasi dengan garis bawah terkemuka di namespace global. Lihat jawaban Roger . Saya akan sangat waspada dengan kutipan MS VC docs sebagai otoritas pada standar C ++.
sbi
@ sbi yang saya maksud adalah "Anda dapat menggunakan satu garis bawah sebagai awalan variabel anggota, asalkan diikuti oleh huruf kecil" dalam jawaban ini, yang menjawab pertanyaan pada teks pertanyaan secara langsung dan ringkas, tanpa tenggelam di dinding teks.
hyde
5
Pertama, saya masih menganggap kurangnya petunjuk bahwa aturan yang sama tidak berlaku untuk namespace global. Yang lebih buruk, meskipun, garis bawah yang berdekatan dilarang tidak hanya pada awal, tetapi di mana saja , pengidentifikasi. Jadi jawaban ini tidak hanya menghilangkan fakta, tetapi sebenarnya membuat setidaknya satu klaim yang salah secara aktif. Seperti yang saya katakan, merujuk ke dokumen MSVC adalah sesuatu yang tidak akan saya lakukan kecuali pertanyaannya semata-mata tentang VC.
sbi
25

Adapun bagian lain dari pertanyaan, itu umum untuk menempatkan garis bawah pada akhir nama variabel untuk tidak berbenturan dengan apa pun yang internal.

Saya melakukan ini bahkan di dalam kelas dan ruang nama karena saya hanya perlu mengingat satu aturan (dibandingkan dengan "di akhir nama dalam lingkup global, dan awal nama di tempat lain").

Max Lybbert
sumber
2

Ya, garis bawah dapat digunakan di mana saja di pengidentifikasi. Saya percaya aturannya adalah: salah satu dari az, AZ, _ dalam karakter pertama dan yang 0-9 untuk karakter berikut

Awalan garis bawah umum dalam kode C - garis bawah tunggal berarti "pribadi", dan garis bawah ganda biasanya dicadangkan untuk digunakan oleh kompiler.

John Millikin
sumber
3
Mereka umum di perpustakaan. Mereka seharusnya tidak umum dalam kode pengguna.
Martin York
43
Orang - orang menulis perpustakaan di C, Anda tahu.
John Millikin
7
"Ya, garis bawah dapat digunakan di mana saja di pengidentifikasi." Ini salah untuk pengidentifikasi global. Lihat jawaban Roger .
sbi