Katakanlah saya memiliki tipe dan saya ingin menjadikan konstruktor defaultnya pribadi. Saya menulis yang berikut ini:
class C {
C() = default;
};
int main() {
C c; // error: C::C() is private within this context (g++)
// error: calling a private constructor of class 'C' (clang++)
// error C2248: 'C::C' cannot access private member declared in class 'C' (MSVC)
auto c2 = C(); // error: as above
}
Bagus.
Tapi kemudian, konstruktornya ternyata tidak sepribadi yang saya kira:
class C {
C() = default;
};
int main() {
C c{}; // OK on all compilers
auto c2 = C{}; // OK on all compilers
}
Ini menurut saya sebagai perilaku yang sangat mengejutkan, tidak terduga, dan secara eksplisit tidak diinginkan. Mengapa ini oke?
C c{};
inisialisasi agregat jadi tidak ada konstruktor yang dipanggil?C
juga dengan agregat.=default
ctor publik , itu akan tampak lebih masuk akal. Tapi private=default
ctor sepertinya merupakan hal penting yang tidak boleh diabaikan. Terlebih lagi,class C { C(); } inline C::C()=default;
menjadi sangat berbeda agak mengejutkan.Jawaban:
Triknya ada di C ++ 14 8.4.2 / 5 [dcl.fct.def.default]:
Artinya
C
, konstruktor default sebenarnya tidak disediakan oleh pengguna, karena ini secara eksplisit default pada deklarasi pertamanya. Dengan demikian,C
tidak memiliki konstruktor yang disediakan pengguna dan oleh karena itu merupakan agregat per 8.5.1 / 1 [dcl.init.aggr]:sumber
C{}
bekerja bahkan jika konstruktordelete
d.Anda tidak memanggil konstruktor default, Anda menggunakan inisialisasi agregat pada jenis agregat. Jenis agregat diizinkan untuk memiliki konstruktor default, selama itu default di tempat pertama kali dinyatakan:
Dari [dcl.init.aggr] / 1 :
dan dari [dcl.fct.def.default] / 5
Jadi, persyaratan kami untuk agregat adalah:
C
memenuhi semua persyaratan ini.Biasanya, Anda dapat menyingkirkan perilaku konstruksi default palsu ini hanya dengan menyediakan konstruktor default kosong, atau dengan mendefinisikan konstruktor sebagai default setelah mendeklarasikannya:
class C { C(){} }; // --or-- class C { C(); }; inline C::C() = default;
sumber
Angew ini dan jaggedSpire ini jawaban yang sangat baik dan berlaku untukc ++ 11. Danc ++ 14. Danc ++ 17.
Namun, dalam c ++ 20, banyak hal berubah sedikit dan contoh di OP tidak akan lagi dikompilasi:
class C { C() = default; }; C p; // always error auto q = C(); // always error C r{}; // ok on C++11 thru C++17, error on C++20 auto s = C{}; // ok on C++11 thru C++17, error on C++20
Seperti yang ditunjukkan oleh dua jawaban, alasan kedua deklarasi terakhir berfungsi adalah karena
C
merupakan agregat dan ini adalah inisialisasi agregat. Namun, sebagai hasil dari P1008 (menggunakan contoh motivasi yang tidak terlalu berbeda dari OP), definisi agregat berubah dalam C ++ 20 menjadi, dari [dcl.init.aggr] / 1 :Penekanan milikku. Sekarang persyaratannya adalah tidak ada konstruktor yang dideklarasikan pengguna , sedangkan dulu (karena kedua pengguna mengutip jawaban mereka dan dapat dilihat secara historis untuk C ++ 11 , C ++ 14 , dan C ++ 17 ) tidak ada konstruktor yang disediakan pengguna . Konstruktor default untuk
C
dideklarasikan oleh pengguna, tetapi tidak disediakan pengguna, dan karenanya berhenti menjadi agregat di C ++ 20.Berikut adalah contoh ilustrasi lain dari perubahan agregat:
class A { protected: A() { }; }; struct B : A { B() = default; }; auto x = B{};
B
bukan agregat di C ++ 11 atau C ++ 14 karena memiliki kelas dasar. Akibatnya,B{}
cukup panggil konstruktor default (dideklarasikan pengguna tetapi tidak disediakan pengguna), yang memiliki akses keA
konstruktor default yang dilindungi.Dalam C ++ 17, sebagai hasil dari P0017 , agregat diperluas untuk memungkinkan kelas dasar.
B
adalah agregat dalam C ++ 17, yang berartiB{}
inisialisasi agregat yang harus menginisialisasi semua subobjek - termasukA
subobjek. Tetapi karenaA
konstruktor default dilindungi, kami tidak memiliki akses ke sana, jadi inisialisasi ini salah bentuk.Dalam C ++ 20, karena
B
konstruktor yang dideklarasikan oleh pengguna, ia kembali berhenti menjadi agregat, jadiB{}
kembali ke pemanggilan konstruktor default dan ini adalah inisialisasi yang dibentuk dengan baik.sumber