Di antara banyak hal yang Stack Overflow telah ajarkan kepada saya adalah apa yang dikenal sebagai "parse yang paling menjengkelkan", yang secara klasik ditunjukkan dengan garis seperti
A a(B()); //declares a function
Sementara ini, untuk sebagian besar, secara intuitif tampak sebagai deklarasi objek a
bertipe A
, mengambil B
objek sementara sebagai parameter konstruktor, sebenarnya ini merupakan deklarasi fungsi yang a
mengembalikan A
, mengambil pointer ke fungsi yang mengembalikan B
dan itu sendiri tidak mengambil parameter . Begitu pula dengan garis
A a(); //declares a function
juga jatuh di bawah kategori yang sama, karena alih-alih objek, itu menyatakan fungsi. Sekarang, dalam kasus pertama, solusi yang biasa untuk masalah ini adalah dengan menambahkan set kurung / kurung tambahan di sekitar B()
, karena kompiler kemudian akan menafsirkannya sebagai deklarasi objek
A a((B())); //declares an object
Namun, dalam kasus kedua, melakukan hal yang sama mengarah ke kesalahan kompilasi
A a(()); //compile error
Pertanyaan saya adalah, mengapa? Ya, saya sangat menyadari bahwa 'solusi' yang benar adalah mengubahnya A a;
, tetapi saya ingin tahu apa yang dilakukan ekstra ()
untuk kompiler pada contoh pertama yang kemudian tidak berfungsi saat mendaftar ulang di contoh kedua. Apakah A a((B()));
solusinya pengecualian khusus ditulis ke dalam standar?
(B())
hanyalah ekspresi C ++, tidak lebih. Ini bukan pengecualian. Satu-satunya perbedaan yang dibuatnya adalah bahwa tidak mungkin itu dapat diuraikan sebagai tipe, dan karenanya tidak.A a();
adalah tidak dari kategori yang sama. Untuk kompiler , tidak pernah ada cara lain untuk menguraikannya: Penginisialisasi di tempat itu tidak pernah terdiri dari tanda kurung kosong, jadi ini selalu merupakan deklarasi fungsi.A a();
adalah bukan contoh yang paling menjengkelkan parse . Ini hanyalah deklarasi fungsi, seperti halnya di C.A a;
" salah. Itu tidak akan memberi Anda inisialisasi jenis POD. Untuk mendapatkan inisialisasi tulisA a{};
.Jawaban:
Tidak ada jawaban tercerahkan, itu hanya karena itu tidak didefinisikan sebagai sintaks yang valid oleh bahasa C ++ ... Jadi, definisi bahasa.
Jika Anda memiliki ekspresi di dalamnya maka itu valid. Sebagai contoh:
Bahkan lebih sederhana: karena
(x)
merupakan ekspresi C ++ yang valid, sedangkan()
tidak.Untuk mempelajari lebih lanjut tentang bagaimana bahasa didefinisikan, dan bagaimana kompiler bekerja, Anda harus belajar tentang teori bahasa formal atau lebih khusus lagi Tata Bahasa Bebas Konteks (CFG) dan materi terkait seperti mesin keadaan terbatas. Jika Anda tertarik meskipun halaman wikipedia tidak cukup, Anda harus mendapatkan buku.
sumber
(x)
merupakan ekspresi C ++ yang valid, sedangkan()
tidak.Solusi terakhir untuk masalah ini adalah pindah ke sintaks inisialisasi seragam C + 11 jika Anda bisa.
http://www.stroustrup.com/C++11FAQ.html#uniform-init
sumber
Deklarator fungsi C
Pertama-tama, ada C. Dalam C,
A a()
adalah deklarasi fungsi. Misalnya,putchar
memiliki deklarasi berikut. Biasanya, deklarasi semacam itu disimpan dalam file header, namun tidak ada yang menghentikan Anda dari menulisnya secara manual, jika Anda tahu bagaimana deklarasi fungsi itu terlihat. Nama argumen adalah opsional dalam deklarasi, jadi saya menghilangkannya dalam contoh ini.Ini memungkinkan Anda untuk menulis kode seperti ini.
C juga memungkinkan Anda untuk mendefinisikan fungsi yang mengambil fungsi sebagai argumen, dengan sintaks yang dapat dibaca yang terlihat seperti panggilan fungsi (well, ini dapat dibaca, selama Anda tidak akan mengembalikan pointer ke fungsi).
Seperti yang saya sebutkan, C memungkinkan menghilangkan nama argumen dalam file header, oleh karena itu
output_result
akan terlihat seperti ini di file header.Satu argumen dalam konstruktor
Apakah kamu tidak mengenali yang itu? Baiklah, izinkan saya mengingatkan Anda.
Yap, itu deklarasi fungsi yang persis sama.
A
adalahint
,a
adalahoutput_result
, danB
sedangint
.Anda dapat dengan mudah melihat konflik C dengan fitur baru C ++. Tepatnya, konstruktor menjadi nama kelas dan tanda kurung, dan sintaks deklarasi alternatif dengan
()
alih - alih=
. Secara desain, C ++ mencoba untuk kompatibel dengan kode C, dan karena itu harus berurusan dengan kasus ini - bahkan jika praktis tidak ada yang peduli. Oleh karena itu, fitur C lama memiliki prioritas di atas fitur C ++ baru. Tata bahasa deklarasi mencoba mencocokkan nama sebagai fungsi, sebelum kembali ke sintaks baru dengan()
jika gagal.Jika salah satu fitur tersebut tidak ada, atau memiliki sintaks yang berbeda (seperti
{}
dalam C ++ 11), masalah ini tidak akan pernah terjadi untuk sintaksis dengan satu argumen.Sekarang Anda mungkin bertanya mengapa itu
A a((B()))
berhasil. Baiklah, mari kita nyatakanoutput_result
dengan tanda kurung yang tidak berguna.Itu tidak akan berhasil. Tata bahasanya membutuhkan variabel untuk tidak berada dalam tanda kurung.
Namun, C ++ mengharapkan ekspresi standar di sini. Di C ++, Anda dapat menulis kode berikut.
Dan kode berikut.
C ++ mengharapkan ekspresi di dalam tanda kurung di dalam menjadi ... well ... ekspresi, sebagai lawan dari tipe C mengharapkan. Kurung tidak ada artinya di sini. Namun, dengan menyisipkan tanda kurung yang tidak berguna, deklarasi fungsi C tidak cocok, dan sintaks baru dapat dicocokkan dengan benar (yang hanya mengharapkan ekspresi, seperti
2 + 2
).Lebih banyak argumen dalam konstruktor
Tentunya satu argumen itu bagus, tapi bagaimana dengan dua? Bukan karena konstruktor mungkin hanya memiliki satu argumen. Salah satu kelas bawaan yang mengambil dua argumen adalah
std::string
Ini semua baik dan baik-baik saja (secara teknis, itu akan sangat mengurai jika dituliskan
std::string wat(int(), char())
, tetapi mari kita jujur - siapa yang akan menulis itu? Tapi mari kita asumsikan kode ini memiliki masalah yang menjengkelkan. Anda akan menganggap bahwa Anda harus meletakkan semuanya dalam tanda kurung.Tidak juga.
Saya tidak yakin mengapa g ++ mencoba untuk mengkonversi
char
keconst char *
. Either way, konstruktor dipanggil hanya dengan satu nilai tipechar
. Tidak ada overload yang memiliki satu argumen tipechar
, oleh karena itu kompiler bingung. Anda mungkin bertanya - mengapa argumennya bertipe char?Iya,
,
ini operator koma. Operator koma mengambil dua argumen, dan memberikan argumen sisi kanan. Itu tidak benar-benar berguna, tetapi itu sesuatu yang harus diketahui untuk penjelasan saya.Sebagai gantinya, untuk memecahkan parse yang paling menjengkelkan, kode berikut ini diperlukan.
Argumennya dalam tanda kurung, bukan seluruh ekspresi. Faktanya, hanya satu ekspresi yang perlu di dalam tanda kurung, karena cukup untuk sedikit keluar dari tata bahasa C untuk menggunakan fitur C ++. Hal-hal membawa kita ke titik nol argumen.
Tidak ada argumen dalam konstruktor
Anda mungkin telah memperhatikan
eighty_four
fungsi dalam penjelasan saya.Ya, ini juga dipengaruhi oleh parse yang paling menjengkelkan. Ini adalah definisi yang valid, dan kemungkinan besar Anda telah melihat jika Anda membuat file header (dan Anda harus). Menambahkan tanda kurung tidak memperbaikinya.
Kenapa begitu? Ya,
()
itu bukan ekspresi. Di C ++, Anda harus meletakkan ekspresi di antara tanda kurung. Anda tidak dapat menulisauto value = ()
dalam C ++, karena()
tidak berarti apa-apa (dan bahkan jika itu, seperti tuple kosong (lihat Python), itu akan menjadi satu argumen, bukan nol). Secara praktis itu berarti Anda tidak dapat menggunakan sintaks steno tanpa menggunakan sintaks C ++ 11{}
, karena tidak ada ekspresi untuk dimasukkan dalam tanda kurung, dan tata bahasa C untuk deklarasi fungsi akan selalu berlaku.sumber
Anda malah bisa
menggunakan
sumber
int a = int();
menginisialisasia
dengan 0,int a;
meninggalkana
diinisialisasi. Solusi yang benar adalah digunakanA a = {};
untuk agregat,A a;
ketika inisialisasi default melakukan apa yang Anda inginkan, danA a = A();
dalam semua kasus lainnya - atau cukup gunakanA a = A();
secara konsisten. Di C ++ 11, cukup gunakanA a {};
Parens terdalam dalam contoh Anda akan menjadi ekspresi, dan dalam C + + tata bahasa mendefinisikan suatu
expression
menjadiassignment-expression
atau yang lainexpression
diikuti oleh koma dan lainnyaassignment-expression
(Lampiran A.4 - Ringkasan / Ekspresi Grammar).Tata bahasa selanjutnya mendefinisikan suatu
assignment-expression
sebagai salah satu dari beberapa jenis ekspresi lainnya, tidak ada yang bisa apa-apa (atau hanya spasi putih).Jadi alasan Anda tidak dapat memiliki
A a(())
hanya karena tata bahasa tidak mengizinkannya. Namun, saya tidak bisa menjawab mengapa orang-orang yang membuat C ++ tidak mengizinkan penggunaan parens kosong ini sebagai semacam kasus khusus - saya kira mereka lebih suka tidak memasukkan kasus khusus seperti itu jika ada alternatif yang masuk akal.sumber