Member nicht genullt, ein clang ++ Bug?

13

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, wenn T 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 wenn T 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 Wert 0 (null) gesetzt, der als Ausdruck einer ganzzahligen Konstanten verwendet wird, konvertiert in T ;
  •   
  • 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.

    
goodbyeera 21.02.2014, 11:00
quelle

4 Antworten

12

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

%Vor%

... 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.

    
Richard Smith 19.05.2014, 19:39
quelle
6

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

geregelt
  

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

rekursiv initialisieren
  

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.

    
Mike Seymour 21.02.2014 11:37
quelle
5

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

%Vor%

Wenn Sie initialisieren ein B hier,

%Vor%

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.

    
juanchopanza 21.02.2014 11:07
quelle
0

Integrale Typen müssen nicht auf einen Wert wie den eines Nicht-Standardkonstruktors initialisiert werden (da Sie einen Konstruktor angegeben haben)

Ändern Sie Ihren Konstruktor in A() : i(0) {} .

    
Michael 21.02.2014 11:08
quelle

Tags und Links