Const sebelum atau const sesudah?

145

Untuk memulai, Anda mungkin tahu bahwa constdapat digunakan untuk membuat data objek atau pointer tidak dapat dimodifikasi atau keduanya.

const Object* obj; // can't change data
Object* const obj; // can't change pointer
const Object* const obj; // can't change data or pointer

Namun Anda juga dapat menggunakan sintaks:

Object const *obj; // same as const Object* obj;

Satu-satunya hal yang tampaknya penting adalah sisi asterisk mana yang Anda masukkan constkata kunci. Secara pribadi saya lebih suka memakai constdi sebelah kiri jenis untuk menentukan data itu tidak dapat dimodifikasi karena saya menemukan itu membaca lebih baik dalam pola pikir kiri-ke-kanan saya tetapi sintaks mana yang lebih dulu?

Lebih penting lagi mengapa ada dua cara yang benar untuk menentukan constdata dan dalam situasi apa Anda lebih suka atau membutuhkan satu dari yang lain jika ada?

Edit:

Jadi sepertinya ini adalah keputusan yang sewenang-wenang ketika standar untuk bagaimana kompiler harus menafsirkan hal-hal yang dirancang jauh sebelum saya lahir. Karena constditerapkan pada apa yang ada di sebelah kiri kata kunci (secara default?) Saya kira mereka pikir tidak ada salahnya menambahkan "pintasan" untuk menerapkan kata kunci dan mengetikkan kualifikasi dengan cara lain setidaknya sampai saat deklarasi berubah oleh parsing * atau & ...

Ini adalah kasus di C juga maka saya berasumsi?

AJG85
sumber
9
Dalam makro selalu tambahkan const setelah jenis, misalnya #define MAKE_CONST(T) T constbukannya #define MAKE_CONST(T) const Tsehingga MAKE_CONST(int *)akan benar berkembang menjadi int * constbukan const int *.
jotik
6
Saya telah melihat dua gaya ini disebut sebagai "const timur" dan "const barat".
Tom Anderson
13
@ TomAnderson tetapi sebenarnya harus "const timur" dan "const barat".
YSC

Jawaban:

96

mengapa ada dua cara yang benar untuk menentukan constdata dan dalam situasi apa Anda lebih suka atau membutuhkan satu dari yang lain jika ada?

Pada dasarnya, alasan bahwa posisi constdalam specifier sebelum tanda bintang tidak menjadi masalah adalah bahwa tata bahasa C didefinisikan seperti itu oleh Kernighan dan Ritchie.

Alasan mereka mendefinisikan tata bahasa dengan cara ini adalah kemungkinan bahwa kompiler C mereka mem-parsing input dari kiri ke kanan dan selesai memproses masing-masing token saat dikonsumsi. Mengkonsumsi *token mengubah status deklarasi saat ini menjadi tipe pointer. Mengalami constsetelah *berarti constkualifikasi diterapkan ke deklarasi pointer; menjumpainya sebelum *sarana kualifikasi diterapkan pada data yang ditunjukkan.

Karena makna semantik tidak berubah jika constkualifikasi muncul sebelum atau setelah penspesifikasi tipe, itu diterima baik cara.

Jenis kasus serupa muncul ketika mendeklarasikan pointer fungsi, di mana:

  • void * function1(void)mendeklarasikan fungsi yang mengembalikan void *,

  • void (* function2)(void)mendeklarasikan pointer fungsi ke fungsi yang mengembalikan void.

Sekali lagi hal yang perlu diperhatikan adalah bahwa sintaks bahasa mendukung parser kiri-ke-kanan.

Heath Hunnicutt
sumber
7
Kernighan ikut menulis buku itu tetapi tidak terlibat dalam desain C, hanya Ritchie.
Tom Zych
8
Saya tidak pernah bisa mengingat yang mana. Berkat penjelasan Anda, akhirnya saya memiliki mnemotechnic untuk mengingatnya. Terima kasih! Sebelum *kompiler parser tidak tahu itu adalah pointer maka itu adalah const untuk nilai data. Setelah * itu terkait dengan pointer konstan. Cemerlang. Dan akhirnya itu menjelaskan mengapa saya bisa melakukannya const charjuga char const.
Vit Bernatik
2
Dugaan mengapa itu dilakukan dengan cara ini tampaknya agak lemah / bertentangan dengan saya. Yaitu, jika saya mendefinisikan bahasa dan menulis kompiler, dan saya ingin membuatnya tetap sederhana dan "parsing input dari kiri ke kanan dan selesai memproses setiap token saat mengkonsumsinya", seperti yang Anda katakan, menurut saya Saya akan meminta constuntuk selalu datang setelah hal itu memenuhi syarat ... tepat sehingga saya selalu bisa selesai memproses const segera setelah saya mengkonsumsinya. Jadi ini tampaknya menjadi argumen untuk melarang western-const, daripada membiarkannya.
Don Hatch
3
"Karena makna semantik tidak berubah jika kualifikasi const muncul sebelum atau setelah penspesifikasi tipe, itu diterima dengan cara baik." Bukankah itu alasan melingkar? Pertanyaannya adalah mengapa makna semantik didefinisikan seperti itu, jadi saya rasa kalimat ini tidak berkontribusi apa-apa.
Don Hatch
1
@donhatch Anda harus ingat bahwa, relatif terhadap hari ini dan asumsi yang kami buat berdasarkan keakraban kami dengan desain bahasa pemrograman yang baik, bahasa adalah hal yang cukup baru saat itu. Juga, apakah seseorang memiliki bahasa yang permisif atau terbatas adalah penilaian nilai. Misalnya, haruskah python memiliki ++operator? "Kalimatnya", imho, membantuku menyadari bahwa tidak ada alasan khusus selain karena mereka bisa. Mungkin mereka akan membuat pilihan yang berbeda hari ini / mungkin tidak.
ragerdl
75

Aturannya adalah:

const berlaku untuk hal yang tersisa darinya. Jika tidak ada apapun di sebelah kiri maka itu berlaku untuk hal yang benar itu.

Saya lebih suka menggunakan const di sebelah kanan hal menjadi const hanya karena itu adalah konst "asli" cara didefinisikan.

Tapi saya pikir ini adalah sudut pandang yang sangat subyektif.

horstforst
sumber
18
Saya lebih suka meletakkannya di kiri, tapi saya pikir meletakkannya di kanan lebih masuk akal. Anda biasanya membaca tipe C ++ dari kanan ke kiri, misalnya Object const *adalah pointer ke Object const. Jika Anda meletakkan constdi sebelah kiri, itu akan dibaca sebagai pointer ke Obyek yang const, yang tidak benar-benar mengalir dengan baik.
Collin Dauphinee
1
Saya mendapat kesan bahwa di sebelah kiri adalah untuk konsistensi gaya manusia dengan jenis deklarasi C lainnya (menurut komputer itu tidak benar karena constbukan kelas penyimpanan, tetapi orang bukan pengurai).
geekosaur
1
@Heath Saya percaya itu lebih merupakan pedoman daripada aturan dan saya sering mendengarnya sebagai cara mengingat bagaimana kompiler akan menafsirkannya ... Saya mengerti cara kerjanya jadi saya hanya ingin tahu tentang proses pemikiran di balik keputusan untuk mendukung keduanya.
AJG85
3
@HeathHunnicutt aturannya ada, tetapi hanya sedikit lebih rumit: c-faq.com/decl/spiral.anderson.html
imallett
2
@HeathHunnicutt: aturan spiral adalah versi diperluas dari komentar komentator pertama "Anda biasanya membaca tipe [C /] C ++ dari kanan-ke-kiri". Saya kira Anda menentang hal ini. Namun, saya pikir Anda mungkin merujuk pada jawaban itu sendiri.
imallett
56

Saya lebih suka sintaks kedua. Ini membantu saya melacak 'apa' yang konstan dengan membaca deklarasi tipe dari kanan ke kiri:

Object * const obj;        // read right-to-left:  const pointer to Object
Object const * obj;        // read right-to-left:  pointer to const Object
Object const * const obj;  // read right-to-left:  const pointer to const Object
Matt Davis
sumber
3
Persis. "Penunjuk konstan ke objek konstan" Object const* consttidak const const Object*. "const" tidak boleh berada di sebelah kiri kecuali dalam kasus khusus di mana begitu banyak orang benar-benar menyukainya. (Lihat Heath di atas.)
cdunn2001
38

Urutan kata kunci dalam deklarasi tidak semuanya diperbaiki. Ada banyak alternatif untuk "tatanan yang benar". Seperti ini

int long const long unsigned volatile i = 0;

atau seharusnya begitu

volatile unsigned long long int const i = 0;

??

Bo Persson
sumber
25
+1 untuk definisi variabel sederhana yang membingungkan. :)
Xeo
@rubenvb - Ya, sayangnya mereka. Tata bahasanya hanya mengatakan bahwa a decl-specifier-seqadalah urutan decl-specifiers. Tidak ada urutan yang diberikan oleh tata bahasa, dan jumlah kejadian untuk setiap kata kunci hanya dibatasi oleh beberapa aturan semantik (Anda dapat memiliki satu consttetapi dua long:-)
Bo Persson
3
@rubenvb - Ya, unsignedadalah tipe, sama dengan unsigned intdan int unsigned. unsigned longadalah tipe lain, sama dengan unsigned long intdan int long unsigned. Lihat polanya?
Bo Persson
2
@ Menjadi: Saya melihat kekacauan, harus memiliki tiga untuk melihat pola ;). OK, terima kasih
rubenvb
1
Anda dulunya bisa menambahkan statickata-kata yang campur aduk, tetapi baru-baru ini ada kompiler yang mengeluh bahwa staticharus didahulukan.
Mark Lakata
8

Aturan pertama adalah menggunakan format apa pun yang dibutuhkan oleh standar pengkodean lokal Anda. Setelah itu: meletakkan constdi depan tidak akan berakhir kebingungan ketika typedefs terlibat, misalnya:

typedef int* IntPtr;
const IntPtr p1;   // same as int* const p1;

Jika standar pengodean Anda memungkinkan typedef tentang pointer, maka itu benar-benar harus bersikeras menempatkan const setelah tipe. Dalam setiap kasus tetapi ketika diterapkan pada tipe, const harus mengikuti apa yang berlaku untuknya, sehingga koherensi juga berpendapat mendukung const setelah. Tetapi pedoman pengkodean lokal mengalahkan semua ini; perbedaan biasanya tidak cukup penting untuk kembali dan mengubah semua kode yang ada.

James Kanze
sumber
Saya pikir itu mungkin menyoroti alasan mengapa kita tidak memiliki tip pointer dalam standar kita yang agak longgar di toko ini.
AJG85
1
Kebijakan saya (ketika saya bisa memutuskan sendiri) adalah meletakkan const setelah (demi koherensi) dan tidak menggunakan typedef untuk pointer (atau mengetikkan banyak pada umumnya) :-). Dan BTW, string :: iterator vs. string :: const_iterator mungkin harus diperhitungkan dalam keputusan Anda juga. (Hanya untuk membingungkan :-). Tidak ada jawaban yang tepat.)
James Kanze
Ah ya saya bisa memasukkan perilaku const std::string::const_iteratorjuga untuk ukuran yang baik;)
AJG85
2
@JamesKanze - Tunggu sebentar, bantu saya di sini ... Saya tidak melihat kebingungan dalam contoh yang diposting. Apa lagi yang bisa const IntPtr p1berarti selain "pointer integer konstan" (yaitu, "pointer konstan ke integer")? Tidak ada orang yang waras, bahkan tanpa mengetahui bagaimana IntPtrdidefinisikan, akan berpikir bahwa p1itu bisa berubah. Dan dalam hal ini, mengapa ada yang salah menganggap itu *p1tidak berubah? Terlebih lagi, menempatkan const di tempat lain (misalnya, IntPtr const p1), tidak mengubah semantik sama sekali.
Todd Lehman
3
@ToddLehman Anda mungkin tidak melihat kebingungan, tetapi sebagian besar programmer C ++ melakukannya, dan secara sistematis salah melakukannya (tidak diragukan dibantu oleh hal-hal seperti std::vector<T>::const_iterator, di mana itu bukan iterator yang merupakan const, tetapi apa yang ditunjuknya).
James Kanze
7

Ada alasan historis bahwa kiri atau kanan dapat diterima. Stroustrup telah menambahkan const ke C ++ pada tahun 1983 , tetapi tidak membuatnya ke C sampai C89 / C90.

Di C ++ ada alasan bagus untuk selalu menggunakan const di sebelah kanan. Anda akan konsisten di mana saja karena fungsi const member harus dinyatakan dengan cara ini:

int getInt() const;
Nick Westgate
sumber
1
... yah, "alasan bagus" tidak terlalu meyakinkan, karena lokasi lain yang mungkin untuk "const" tidak akan berarti hal yang sama. const int& getInt(); int& const getInt();
Maestro
1
@ Maestro: Saya menyarankan int const& getInt();lebih baik daripada yang setara const int& getInt();sedangkan int& const getInt();Anda membandingkannya dengan berlebihan (referensi sudah const) meskipun legal dan biasanya akan memberikan peringatan. Lagi pula, const pada fungsi anggota mengubah thispointer di fungsi dari Foo* constmenjadi Foo const* const.
Nick Westgate
constpada fungsi anggota tidak berarti sama sekali hal yang sama — atau apakah void set(int)&;semacam referensi ke fungsi?
Davis Herring