2015-04-23 4 views
17

GCC (getestet mit 4.9) akzeptiert den folgenden Testfall:Überlastungs Auflösungsunterschied zwischen gcc und Klappern Einbeziehung move Konstruktor und 'Abgeleitet (Basis &&)' Konstruktor

struct Base {}; 

struct Derived : Base { 
    Derived(); 
    explicit Derived(const Derived&); 
    explicit Derived(Derived&&); 
    explicit Derived(const Base&); 
    Derived(Base&&); 
}; 

Derived foo() { 
    Derived result; 
    return result; 
} 

int main() { 
    Derived result = foo(); 
} 

Clang (getestet mit 3.5) lehnt sie mit folgenden Fehlermeldung:

test.cpp:13:10: error: no matching constructor for initialization of 'Derived' 
    return result; 
     ^~~~~~ 
test.cpp:8:5: note: candidate constructor not viable: no known conversion from 'Derived' to 'Base &&' for 1st argument 
    Derived(Base&&); 
    ^
test.cpp:4:5: note: candidate constructor not viable: requires 0 arguments, but 1 was provided 
    Derived(); 
    ^

Wer hat Recht?

Antwort

14

Ich glaube, Clang ist hier richtig. GCC sollte den Code nicht akzeptieren.

Der Grund dafür ist die Art und Weise der Überladungsauflösung für Konstrukteure für die Objektkopie in einer return Anweisung auftritt in [class.copy] p32 (Hervorhebung von mir) angegeben wird:

Wenn die Kriterien für die Auslassung einer Kopieren/Verschieben Konstruktor erfüllt sind , [...] und das Objekt, das kopiert werden soll, wird mit einem Lvalue, [...], Überladungsauflösung angegeben, um den Konstruktor für die Kopie auszuwählen, wird zuerst so ausgeführt, als ob das Objekt durch einen rvalue gekennzeichnet wäre. Wenn die erste Überlastungs Auflösung ausfällt oder nicht durchgeführt wurde, oder wenn die Art der der erste Parameter des ausgewählten Konstruktor nicht den Typ eines rvalue Referenz auf das Objekt (möglicherweise cv-qualifiziert), überlasten Auflösung erneut ausgeführt wird, unter Berücksichtigung des Objekts als L-Wert.

In diesem Beispiel werden die Kriterien für die elision erfüllen (durch die erste Kugel in [class.copy] p31) und das Objekt wird von einem L-Wert kopiert bezeichnet, so gilt dieser Absatz.

Die Überladungsauflösung wird zuerst versucht, als ob das Objekt mit einem R-Wert gekennzeichnet wäre. Die explicit Konstruktoren sind keine Kandidaten (siehe unten für eine Erklärung des Warum), so dass der Derived(Base&&) Konstruktor ausgewählt ist. Dies fällt jedoch unter "der Typ des ersten Parameters des ausgewählten Konstruktors ist kein rvalue-Verweis auf den Objekttyp" (stattdessen ist es ein rvalue-Verweis auf den Typ der Basisklasse des Objekts), sodass die Überladungsauflösung erneut ausgeführt werden sollte unter Berücksichtigung des Objekts als L-Wert.

Diese zweite Überladungsauflösung schlägt fehl, da der einzige ausführbare Konstruktor (wiederum die explicit-Konstruktoren keine Kandidaten sind) einen Rvalue-Referenzparameter hat, der nicht an den Lvalue binden kann. Clang zeigt den resultierenden Fehlerfehler bei der Überlastungsauflösung an.


die Erklärung abzuschließen, ist hier, warum explicit Konstrukteure entweder keine Kandidaten für eine Auflösung Überlastung sind (alle Betonung von mir).

Zuerst [dcl.init] p15 sagt, dass:

Die Initialisierung, die in der = Form eines Klammer-oder-gleich-Initialisierer oder Zustand (6,4), sowie in Argumente auftritt Passing, Funktion zurückgeben, werfen eine Ausnahme (15.1), Umgang mit einer Ausnahme (15.3) und Aggregatelement Initialisierung (8.5.. 1), ist genannt copy-Initialisierung

Als nächstes schauen wir auf [over.match.ctor] p1:

Für Copy-Initialisierung sind die Kandidaten Funktionen alle Umwandlung Konstruktoren (12.3.1) dieser Klasse

Schließlich

, sehen wir, dass explicit Konstrukteuren nicht Konstruktoren in [class.conv.ctor] p1 konvertieren:

Ein Konstruktor erklärt ohne funktions Spezifizierer explicit Spezifiziert eine Umwandlung von den Arten der Parameter auf die Art ihrer Klasse. Ein solcher Konstruktor wird genannt, der Konstruktor umwandelt.

+0

Aber es sagt "wenn der Typ, wenn der erste Parameter keine Rvalue-Referenz ist, wird es erneut durchgeführt". Daher wird die Überladungsauflösung nicht erneut durchgeführt. – Columbo

+0

@Columbo: Wenn "der Typ des ersten Parameters keine rvalue-Referenz ** für den Objekttyp **" ist, wird die Überladungsauflösung erneut ausgeführt. Hier handelt es sich um eine rvalue-Referenz, aber nicht um den Typ des Objekts. (Beachten Sie, dass sich "das Objekt" hier auf "das zu kopierende Objekt" vom Anfang des Absatzes bezieht, das ein Objekt vom Typ "Abgeleitet" ist.) – HighCommander4

+0

Ahh, richtig, richtig. – Columbo

Verwandte Themen