6

Bedenken Sie:Einfaches Programm einheitliche Initialisierung mit einem Objekt zu konstruieren versagt das folgende Programm zu kompilieren

struct X 
{ 
    X(int, int) { } 
    X(X&&) { } 
}; 

int main() 
{ 
    X x({0, 1}); // Doesn't compile on ICC 13.0.1, compiles on 
        // Clang 3.2, GCC 4.7.2, and GCC 4.8.0 beta. 
} 

Wenn mit GCC kompiliert 4.7.2, GCC 4.8.0 und Clang 3.2 Dieses Programm führt folgende (*):

  1. Konstruiert eine temporäre vom Typ X Gabe von Werten 01 und an den Konstruktor, dann;
  2. Move-Konstrukte X von diesem temporären.

Mit ICC 13.0.1 wird es stattdessen nicht kompiliert.

FRAGE # 1: Wer hat Recht?

(*) Eigentlich ist die Erstellung der temporären und der Anruf an den Umzug Konstruktor elided werden, aber mit der -fno-elide-constructors Option kompilieren und einige Ausdrucke der Konstrukteurs Zugabe zeigt, dass das ist, was los ist.


nun die folgende, leichte Variation des obigen Programms prüfen, wo einheitliche Initialisierung verwendet wird direkt initialisieren x:

int main() 
{ 
    X x{ {0, 1} }; // ERROR! Doesn't compile. 
//  ^........^ 
} 

ich die Verwendung von Klammern nicht erwarten würde statt Klammern, um irgendetwas hier zu ändern, aber es tut irgendwie: Dieses Programm kompiliert nicht auf irgendwelcher der Compiler, die ich es getestet habe (Clang 3.2, GCC 4.7.2, GCC 4.8.0 Beta und ICC 13.0.1).

FRAGE # 2: Warum?

+0

Ich bin gespannt, welche Fehler Sie mit dem zweiten bekommen. –

+0

@ NicolBolas: Überprüfen Sie es [hier] (http://liveworkspace.org/code/10CpDi$73), können Sie den Compiler wählen. –

+0

Sie waren lange genug hier, um zu wissen, dass SO nicht so funktioniert. Wenn es relevant ist, stellst du es in die Frage, nicht auf eine Live-Workspace-Seite zu verlinken. –

Antwort

2

Es ist einfach ein Fehler in allen Compilern. §8.5.4/3 sagt,

List-Initialisierung eines Objekts oder eine Referenz vom Typ T wird wie folgt definiert:

- Wenn die Initialisiererliste hat keine Elemente und T ein Klassentyp mit einem Standardkonstruktor, ist das Objekt Wert initialisiert.

- Andernfalls, wenn T ein Aggregat ist, wird die Aggregatinitialisierung durchgeführt (8.5.1).

- Andernfalls, wenn T eine Spezialisierung von std :: initializer_list ist, ein initializer_list Objekt wird, wie unten beschrieben aufgebaut und verwendet, um das Objekt zu initialisieren ...

- Andernfalls, wenn T ein Klassentyp ist, Konstrukteure ist berücksichtigt. Die anwendbaren Konstruktoren werden aufgelistet und der beste wird durch die Überladungsauflösung ausgewählt (13.3, 13.3.1.7). Wenn eine konvergierende Konvertierung (siehe unten) erforderlich ist, um eines der Argumente zu konvertieren, ist das Programm schlecht formatiert.

- Andernfalls, wenn T ein Referenztyp ist, wird ein temporärer prvalue des durch T referenzierten Typs in der Liste initialisiert, und die Referenz ist an dieses temporäre gebunden.

- Andernfalls, wenn die Initialisierungsliste ein einzelnes Element hat, wird das Objekt oder die Referenz von diesem Element initialisiert; Wenn eine konvergierende Konvertierung (siehe unten) erforderlich ist, um das Element in T umzuwandeln, ist das Programm schlecht formatiert.

...

Es gibt ganz wenige Fälle. Beachten Sie, dass Sie ein prvalue-temporäres Objekt, das an die rvalue-Referenz gebunden ist, in der Liste initialisieren.

Wie für GCC, ich denke, es versucht, das letzte Element, das oben erwähnt wird, für eine Ein-Element-Initialisierungsliste anzuwenden. Wenn ich die Konstruktorsignatur in X(X&&, int = 3) ändere, schlägt der Initialisierer { {0, 1} } fehl, aber { {0, 1}, 3 } ist erfolgreich. Das einzelne Element sollte erfolgreich sein, weil das Element eine brained-init-Liste ist, und ich glaube, dass dieser Fall zusätzliche umschließende Klammern zulassen soll, analog zu parens. Aber das Versagen ist ähnlich zu anderen Mängeln von GCC mit Klammer Elision.

Meine High-Level-Eindruck ist, dass die Probleme auftreten, wenn der Compiler versucht, die Liste als ein Objekt mit einem Typ zu behandeln, die es nicht ist. Es ist schwierig, das wieder in eine Klammerlisten-Liste umzuwandeln.

Suche genauer gesagt an den Fehlermeldungen (vielen Dank für die LWS-Link),

  • ICC besteht darauf, es einen Ausdruck erwartet. Das ist falsch, weil gemäß der grundlegenden Grammatik stützed-init-Listen andere starre-init-Listen einschließen können, nicht nur Ausdrücke.

  • Clang sagt „Kandidaten-Konstruktor nicht lebensfähig: kann nicht Initialisiererliste Argument auf‚X‘konvertieren“ aber es funktioniert, wenn die Konvertierung explizit ist, mit X x{ X{0, 0 } };. Das ergibt keinen Sinn. Es handelt sich nicht um eine Konvertierung, da für eine Liste kein Typ zum Konvertieren vorhanden ist. Es ist Listen-Initialisierung.

  • GCC sagt „keine bekannte Umwandlung für Argument 1 von‚‘auf‚X & &‘“ was darauf hindeutet, es nicht einmal auf den Punkt haben eine temporäre definieren auf die Referenz zu binden. Wie bei Clang scheint es eine unechte Konvertierung zu geben, und die Angabe X{0,0} behebt es.

+0

Hier konzentrieren Sie sich nur auf das zweite Beispiel, oder? –

+0

@AndyProwl Ja. Ich habe keine ICC und ich denke das erste Beispiel ist korrekt. (In der Tat, ich denke, der zweite ist auch.) – Potatoswatter

+0

Sie denken also, dass beide Programme kompilieren sollten? (BTW, [hier] (http://liveworkspace.org/code/10CpDi$87) können Sie ICC auswählen) –

Verwandte Themen