Betrachten Sie den folgenden Code:
%Vor% Kompiliert mit -std = c ++ 11 in clang ++, p->i
stellt sich als Null heraus, aber p->a.i
nicht. Sollte das ganze Objekt nicht genullt werden, solange seine Klasse keinen vom Benutzer bereitgestellten Konstruktor hat?
EDIT: Da es in den Kommentaren einige ausführliche Diskussionen gibt, ist es besser, hier einen Auszug aus dem Standard hinzuzufügen:
Um ein Objekt des Typs
T
zu value-initialisieren, bedeutet:
- Wenn
T
ein (möglicherweise cv-qualifizierter) Klassentyp (Klausel 9) mit einem vom Benutzer bereitgestellten Konstruktor (12.1) ist, wird der Standardkonstruktor für T aufgerufen (und die Initialisierung ist fehlerhaft, wennT
hat keinen zugreifbaren Standardkonstruktor);- Wenn
T
ein (möglicherweise cv-qualifizierter) Nicht-Union-Klassentyp ohne einen vom Benutzer bereitgestellten Konstruktor ist, dann wird das Objekt auf Null initialisiert, und wennT
implizit deklarierter Standardkonstruktor ist, trivial, dieser Konstruktor wird aufgerufen.- Wenn
T
ein Array-Typ ist, wird jedes Element initialisiert.- Andernfalls wird das Objekt initialisiert.
Um ein Objekt oder eine Referenz des Typs T auf Null zu initialisieren, bedeutet:
- Wenn
T
ein Skalartyp (3.9) ist, wird das Objekt auf den Wert0
(null) gesetzt, der als Ausdruck einer ganzzahligen Konstanten verwendet wird, konvertiert inT
;- Wenn
T
ein (möglicherweise cv-qualifizierter) Nicht-Union-Klassentyp ist, wird jedes nicht statische Datenelement und jedes Basisklassen-Unterobjekt auf Null initialisiert, und das Auffüllen wird auf Null Bits initialisiert;- Wenn
T
ein (möglicherweise cv-qualifizierter) Union-Typ ist, wird das erste nicht statische Datenelement des Objekts null initialisiert und das Padding wird auf null Bits initialisiert;- Wenn
T
ein Array-Typ ist, wird jedes Element auf Null initialisiert;- Wenn
T
ein Referenztyp ist, wird keine Initialisierung durchgeführt.
Die zweite Kugel von jedem gilt hier.
Clang ist korrekt, entsprechend dem C ++ 11-Standard und relevanten DRs
In der ursprünglichen C ++ 11-Spezifikation führte B{}
eine Werte-Initialisierung durch, was dazu führte, dass a.i
auf Null initialisiert wurde. Dies war eine Änderung im Verhalten im Vergleich zu C ++ 98 für Fälle wie
... die als Aggregat-Initialisierung in C ++ 98 behandelt wurden, aber in C ++ 11 FDIS als Wert-Initialisierung behandelt wurden.
Allerdings wurde das Verhalten in diesem Fall von Kernproblem 1301 , das das Verhalten von C ++ 98 wiederherstellt, indem es vorschreibt, dass die Aggregat-Initialisierung immer dann verwendet wird, wenn ein Aggregat von einer braced-init-Liste initialisiert wird. Da dieses Problem als DR angesehen wird, wird es bei früheren Versionen des C ++ - Standards als de facto behandelt, sodass von einem konformen C ++ 11-Compiler erwartet wird, dass er hier eine Aggregat-Initialisierung vornimmt. Initialisierung.
Letztlich ist es eine schlechte Idee, sich auf die Initialisierung von Werten zu verlassen, um Ihre Datenelemente zu initialisieren, insbesondere für eine Klasse mit Konstruktoren, die vom Benutzer bereitgestellt werden.
Es sieht in der Tat aus wie ein Fehler (oder, wie in den Kommentaren erwähnt, trotz der Angabe von C ++ 11 gemäß C ++ 03). In C ++ 11 sollte value-initialization die Mitglieder von a
vor dem Aufruf ihres Standardkonstruktors auf Null setzen. Die Initialisierung von B
wird durch diese Regel von 8.5 / 7
Wenn T ein (möglicherweise cv-qualifizierter) Nicht-Vereinigungsklassen-Typ ohne einen vom Benutzer bereitgestellten Konstruktor ist, dann wird das Objekt auf Null initialisiert und, falls T implizit deklariert ist, der Standardkonstruktor nicht -trivial, dieser Konstruktor wird aufgerufen.
Die Null-Initialisierung sollte a
nach dieser Regel von 8.5 / 5
Wenn T ein (möglicherweise cv-qualifizierter) Nicht-Union-Klassentyp ist, wird jedes nicht statische Datenelement und jedes Basisklassen-Unterobjekt auf Null initialisiert
und natürlich die Null-Initialisierung von a
sollte i
auf Null setzen.
Es ist nicht ein Compilerfehler , es ist ein Fehler in Ihrem Code . Der Compiler scheint das C ++ 03-Verhalten zu implementieren, was sich jedoch in C ++ 11 entscheidend geändert hat.
Dies sind einige relevante Zitate aus den C ++ 03 und C ++ 11 Standards
In C ++ 03:
Um ein Objekt vom Typ T zu value-initialisieren, bedeutet:
- wenn T ein Klassentyp ist (Klausel 9) mit einem user-deklarierten Konstruktor (12.1), dann der Standardwert Konstruktor für T wird aufgerufen (und die Initialisierung ist fehlerhaft, wenn T hat keinen zugänglichen Standardkonstruktor);
- wenn T eine nicht gewerkschaftliche Klasse ist Geben Sie ohne einen benutzerdefinierten Konstruktor und dann alle nicht statischen Daten ein Die Element- und Basisklassenkomponente von T wird mit Wert initialisiert;
(Betonung meiner)
In C ++ 11:
Um ein Objekt vom Typ T zu value-initialisieren, bedeutet:
- wenn T ein (möglicherweise cv-qualified) Klassentyp (Klausel 9) mit einem vom Benutzer bereitgestellten Konstruktor (12.1), dann wird der Standardkonstruktor für T aufgerufen (und der Die Initialisierung ist fehlerhaft, wenn T keine verfügbare Standardeinstellung hat Konstrukteur);
- wenn T ein (möglicherweise cv-qualifizierter) Nicht-Union-Klassentyp ist Ohne einen vom Benutzer bereitgestellten Konstruktor, dann ist das Objekt zero-initialized und, falls T implizit als Standardkonstruktor deklariert ist ist nicht-trivial, dieser Konstruktor heißt.
und
Um ein Objekt oder eine Referenz des Typs T auf Null zu initialisieren, bedeutet:
- wenn T ist a Skalartyp (3.9), wird das Objekt auf den Wert 0 (Null) gesetzt ein ganzzahliger konstanter Ausdruck, umgewandelt in T;
- wenn T ein ist (möglicherweise cv-qualifiziert) Nicht-Union-Klassenart, jede nicht statische Daten Mitglied und jedes Unterobjekt der Basisklasse ist initialisiert und padding wird auf Null Bits initialisiert;
Hinweis: Folgendes gilt nur für C ++ 03 :
Entweder entfernen Sie den vom Benutzer bereitgestellten Konstruktor von A
, oder ändern Sie ihn in
Wenn Sie initialisieren ein B
hier,
it value-initialisiert seine Datenelemente. Da A
einen Standardkonstruktor hat, führt die Initialisierung des Werts zu einem Aufruf an diesen Wert. Aber dieser Konstruktor initialisiert A::i
nicht explizit, so dass default-initialisiert wird, was für int
bedeutet, dass keine Initialisierung ausgeführt wird.
Wenn Sie keinen Standardkonstruktor für A
, dann bereitgestellt hätten, würde das Datenelement bei der Initialisierung von A
initialisiert werden.