Sebagai tindak lanjut yang menarik (meskipun tidak terlalu penting secara praktis) untuk pertanyaan saya sebelumnya: Mengapa C ++ memungkinkan kita untuk mengapit nama variabel dalam tanda kurung saat mendeklarasikan variabel?
Saya menemukan bahwa menggabungkan deklarasi dalam tanda kurung dengan fitur nama kelas yang disuntikkan dapat menyebabkan hasil yang mengejutkan terkait perilaku kompilator.
Lihat program berikut:
#include <iostream>
struct B
{
};
struct C
{
C (){ std::cout << "C" << '\n'; }
C (B *) { std::cout << "C (B *)" << '\n';}
};
B *y = nullptr;
int main()
{
C::C (y);
}
Mengompilasi dengan g ++ 4.9.2 memberi saya kesalahan kompilasi berikut:
main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
Ini berhasil dikompilasi dengan MSVC2013 / 2015 dan mencetak
C (B *)
Ini berhasil dikompilasi dengan clang 3.5 dan cetakan
C
Jadi pertanyaan wajibnya adalah mana yang benar? :)
(Saya sangat bergoyang ke arah versi clang dan cara pnidui untuk berhenti mendeklarasikan variabel setelah hanya mengubah tipe dengan secara teknis typedef-nya tampak agak aneh)
sumber
C::C y;
tidak masuk akal, bukan? Begitu pulaC::C (y);
pada awalnya saya pikir ini adalah contoh dari Most-Vexing-Parse stackoverflow.com/questions/tagged/most-vexing-parse , tetapi sekarang saya pikir itu hanya perilaku tidak terdefinisi yang berarti ketiga kompiler "benar".C::C
tidak menamai jenis, ia menamai suatu fungsi, jadi GCC tepat.Jawaban:
GCC benar, setidaknya sesuai dengan aturan pencarian C ++ 11. 3.4.3.1 [class.qual] / 2 menetapkan bahwa, jika penentu nama yang disarangkan sama dengan nama kelas, ini merujuk ke konstruktor bukan nama kelas yang dimasukkan. Ini memberi contoh:
Sepertinya MSVC salah menafsirkannya sebagai ekspresi cor gaya-fungsi yang membuat sementara
C
dengany
sebagai parameter konstruktor; dan Clang salah mengartikannya sebagai deklarasi variabel yang disebuty
tipeC
.sumber
A::A a;
, nama fungsi harus diabaikan - atau tidak?C (B *)
" .G ++ benar karena memberikan kesalahan. Karena konstruktor tidak dapat dipanggil secara langsung dalam format seperti itu tanpa
new
operator. Dan meskipun kode Anda memanggilC::C
, ini terlihat seperti panggilan konstruktor. Namun, menurut C ++ 11 standar 3.4.3.1, ini bukan panggilan fungsi legal, atau nama tipe ( lihat jawaban Mike Seymour ).Dentang salah karena tidak memanggil fungsi yang benar.
MSVC adalah sesuatu yang masuk akal, tetapi tetap saja tidak mengikuti standar.
sumber
new
diubah operator?new B(1,2,3)
semacam "panggilan konstruktor langsung" (yang, tentu saja, bukan) sebagai perbedaan dari contoh sementaraB(1,2,3)
atau deklarasiB b(1,2,3)
.new B(1,2,3)
itu?new
, nama tipe, dan daftar argumen konstruktor. Ini masih bukan "panggilan konstruktor langsung".C::C (y);
sebagaiC::C y;
, yaitu definisi variabel y tipe C (menggunakan tipe C yang diinjeksi: : C sementara secara keliru mengabaikan spesifikasi bahasa 3.4.1,2 yang semakin gila yang membuat C :: C sebagai konstruktor). Itu bukan kesalahan yang mencolok seperti yang Anda pikirkan, imo.