Powiedzmy, że mam typ i chcę ustawić jego domyślny konstruktor jako prywatny. Piszę co następuje:
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
}
Świetny.
Ale potem konstruktor okazuje się nie być tak prywatny, jak myślałem:
class C {
C() = default;
};
int main() {
C c{}; // OK on all compilers
auto c2 = C{}; // OK on all compilers
}
Wydaje mi się to bardzo zaskakujące, nieoczekiwane i wyraźnie niepożądane zachowanie. Dlaczego to jest w porządku?
C c{};
inicjalizacja agregacji, więc żaden konstruktor nie jest wywoływany?C
jest to agregacja.=default
doradca, wydawałoby się to bardziej rozsądne. Ale prywatny=default
doradca wydaje się ważną rzeczą, której nie należy ignorować. Co więcej,class C { C(); } inline C::C()=default;
bycie całkiem innym jest nieco zaskakujące.Odpowiedzi:
Sztuczka jest w C ++ 14 8.4.2 / 5 [dcl.fct.def.default]:
Co oznacza, że
C
domyślny konstruktor w rzeczywistości nie jest dostarczany przez użytkownika, ponieważ został jawnie ustawiony jako domyślny przy pierwszej deklaracji. W związku z tymC
nie ma konstruktorów dostarczonych przez użytkownika i dlatego jest agregatem według 8.5.1 / 1 [dcl.init.aggr]:źródło
C{}
działa, nawet jeśli konstruktorem jestdelete
d.Nie wywołujesz domyślnego konstruktora, używasz inicjalizacji agregacji na typie agregacji. Typy agregatów mogą mieć domyślny konstruktor, o ile jest on domyślny w miejscu, w którym został zadeklarowany po raz pierwszy:
Z [dcl.init.aggr] / 1 :
i z [dcl.fct.def.default] / 5
Zatem nasze wymagania dotyczące kruszywa są następujące:
C
spełnia wszystkie te wymagania.Oczywiście możesz pozbyć się tego fałszywego domyślnego zachowania konstrukcji, po prostu dostarczając pustego domyślnego konstruktora lub definiując konstruktor jako domyślny po zadeklarowaniu go:
class C { C(){} }; // --or-- class C { C(); }; inline C::C() = default;
źródło
Angew's i jaggedSpire są doskonałe i odnoszą się doc ++ 11. Ic ++ 14. Ic ++ 17.
Jednak w c ++ 20, rzeczy się trochę zmieniają i przykład w PO nie będzie się już kompilować:
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
Jak wskazano w dwóch odpowiedziach, powodem, dla którego dwie ostatnie deklaracje działają, jest to, że
C
jest to agregacja, a jest to inicjalizacja agregacji. Jednak w wyniku P1008 (przy użyciu motywującego przykładu nie różniącego się zbytnio od PO), definicja zagregowanych zmian w C ++ 20 do, z [dcl.init.aggr] / 1 :Podkreśl moje. Teraz wymaganiem jest brak konstruktorów zadeklarowanych przez użytkownika , podczas gdy kiedyś (jak obaj użytkownicy cytują w swoich odpowiedziach i można je przeglądać historycznie dla C ++ 11 , C ++ 14 i C ++ 17 ) nie było konstruktorów dostarczonych przez użytkownika . Domyślny konstruktor dla
C
jest zadeklarowany przez użytkownika, ale nie jest dostarczany przez użytkownika, dlatego przestaje być agregacją w C ++ 20.Oto kolejny przykład ilustrujący zagregowane zmiany:
class A { protected: A() { }; }; struct B : A { B() = default; }; auto x = B{};
B
nie był agregatem w C ++ 11 ani C ++ 14, ponieważ ma klasę bazową. W rezultacieB{}
po prostu wywołuje domyślny konstruktor (zadeklarowany przez użytkownika, ale nie dostarczony przez użytkownika), który ma dostęp doA
chronionego konstruktora domyślnego.W C ++ 17, w wyniku P0017 , agregaty zostały rozszerzone o klasy bazowe.
B
jest agregatem w C ++ 17, co oznacza, żeB{}
jest to inicjalizacja agregatu, która musi zainicjować wszystkie podobiekty - w tymA
podobiekt. Ale ponieważA
domyślny konstruktor jest chroniony, nie mamy do niego dostępu, więc ta inicjalizacja jest źle sformułowana.W C ++ 20, z powodu
B
konstruktora zadeklarowanego przez użytkownika, ponownie przestaje być agregatem, więcB{}
powraca do wywoływania domyślnego konstruktora i jest to ponownie dobrze sformułowana inicjalizacja.źródło