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.
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
@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
Ahh, richtig, richtig. – Columbo