Angenommen, ich habe einen Typ und möchte seinen Standardkonstruktor privat machen. Ich schreibe folgendes:
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
}
Toll.
Aber dann stellt sich heraus, dass der Konstruktor nicht so privat ist, wie ich dachte:
class C {
C() = default;
};
int main() {
C c{}; // OK on all compilers
auto c2 = C{}; // OK on all compilers
}
Dies erscheint mir als sehr überraschendes, unerwartetes und explizit unerwünschtes Verhalten. Warum ist das in Ordnung?
C c{};
aggregierte Initialisierung nicht so, dass kein Konstruktor aufgerufen wird?C
ein Aggregat.=default
Ctor wäre, würde das vernünftiger erscheinen. Aber der private=default
Ctor scheint eine wichtige Sache zu sein, die nicht ignoriert werden sollte. Was mehr ist ,class C { C(); } inline C::C()=default;
ganz anders zu sein , ist etwas überraschend.Antworten:
Der Trick ist in C ++ 14 8.4.2 / 5 [dcl.fct.def.default]:
Dies bedeutet, dass
C
der Standardkonstruktor des Benutzers nicht vom Benutzer bereitgestellt wird, da er bei seiner ersten Deklaration explizit als Standard festgelegt wurde.C
Hat als solches keine vom Benutzer bereitgestellten Konstruktoren und ist daher ein Aggregat gemäß 8.5.1 / 1 [dcl.init.aggr]:quelle
C{}
funktioniert, auch wenn der Konstruktordelete
d ist.Sie rufen nicht den Standardkonstruktor auf, sondern verwenden die Aggregatinitialisierung für einen Aggregattyp. Aggregattypen dürfen einen voreingestellten Konstruktor haben, sofern dieser standardmäßig dort ist, wo er zuerst deklariert wurde:
Aus [dcl.init.aggr] / 1 :
und aus [dcl.fct.def.default] / 5
Daher sind unsere Anforderungen an ein Aggregat:
C
erfüllt alle diese Anforderungen.Natürlich können Sie dieses falsche Standardkonstruktionsverhalten beseitigen, indem Sie einfach einen leeren Standardkonstruktor angeben oder den Konstruktor nach der Deklaration als Standard definieren:
quelle
Die Antworten von Angew und jaggedSpire sind ausgezeichnet und gelten fürc ++ 11. Undc ++ 14. Undc ++ 17.
In c ++ 20Die Dinge ändern sich ein wenig und das Beispiel im OP wird nicht mehr kompiliert:
Wie aus den beiden Antworten hervorgeht, funktionieren die beiden letztgenannten Deklarationen, weil
C
es sich um ein Aggregat handelt und es sich um eine Aggregatinitialisierung handelt. Aufgrund von P1008 (anhand eines motivierenden Beispiels, das sich nicht allzu sehr vom OP unterscheidet) ändert sich die Definition der aggregierten Änderungen in C ++ 20 von [dcl.init.aggr] / 1 :Hervorhebung von mir. Jetzt sind keine vom Benutzer deklarierten Konstruktoren mehr erforderlich , während es früher (wie beide Benutzer in ihren Antworten zitieren und historisch für C ++ 11 , C ++ 14 und C ++ 17 angezeigt werden können ) keine vom Benutzer bereitgestellten Konstruktoren war . Der Standardkonstruktor für
C
ist vom Benutzer deklariert, aber nicht vom Benutzer bereitgestellt und ist daher in C ++ 20 kein Aggregat mehr.Hier ist ein weiteres anschauliches Beispiel für aggregierte Änderungen:
B
war kein Aggregat in C ++ 11 oder C ++ 14, da es eine Basisklasse hat. Als ErgebnisB{}
ruft nur den Standard - Konstruktor ( vom Benutzer deklariert , aber nicht vom Benutzer zur Verfügung gestellt), der Zugriff hatA
‚s geschützten Standardkonstruktor.In C ++ 17 wurden als Ergebnis von P0017 Aggregate erweitert, um Basisklassen zu ermöglichen.
B
ist ein Aggregat in C ++ 17, dh esB{}
handelt sich um eine Aggregatinitialisierung, bei der alle Unterobjekte - einschließlich desA
Unterobjekts - initialisiert werden müssen . DaA
der Standardkonstruktor jedoch geschützt ist, haben wir keinen Zugriff darauf, sodass diese Initialisierung fehlerhaft ist.In C ++ 20 ist der vom
B
Benutzer deklarierte Konstruktor aufgrund des vom Benutzer deklarierten Konstruktors wieder kein AggregatB{}
mehr. Daher wird wieder der Standardkonstruktor aufgerufen, und dies ist wiederum eine wohlgeformte Initialisierung.quelle