2013-04-06 11 views
20

Ich habe diesen CodeCompiler denkt, dass "A (A &)" rvalues ​​für einen Moment akzeptiert?

struct A { A(); A(A&); }; 
struct B { B(const A&); }; 

void f(A); 
void f(B); 

int main() { 
    f(A()); 
} 

Zu meiner Überraschung dies mit GCC und Clang ausfällt. Clang sagt zum Beispiel

Compilation finished with errors: 
source.cpp:8:10: error: no matching constructor for initialization of 'A' 
     f(A()); 
     ^~~ 
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument 
    struct A { A(); A(A&); }; 
        ^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided 
    struct A { A(); A(A&); }; 
      ^
source.cpp:4:13: note: passing argument to parameter here 
    void f(A); 

Warum wählen sie die erste f, wenn der zweite f funktioniert gut? Wenn ich die erste f entferne, ist der Anruf erfolgreich. Was ist seltsam für mich, wenn ich Klammer Initialisierung verwenden, es funktioniert auch

int main() { 
    f({A()}); 
} 

Sie alle zweiten f nennen.

Antwort

17

Es ist eine Sprache Eigenart. Die erste f stimmt besser überein, da Ihre A keine Konvertierung erfordert, um dem Argumenttyp (A) zu entsprechen, aber wenn der Compiler versucht, den Aufruf auszuführen, die Tatsache, dass kein geeigneter Kopierkonstruktor gefunden werden kann, verursacht den Aufruf fehlschlagen. Die Sprache erlaubt es nicht, die Durchführbarkeit des tatsächlichen Anrufs bei der Durchführung des Überlastungsauflösungsschritts zu berücksichtigen.

nächste passende Standard Zitat ISO/IEC 14882: 2011 13.3.3.1.2 Benutzerdefinierte Konvertierungssequenzen [over.ics.user]:

Eine Umwandlung eines Ausdrucks der Klassentyp der gleichen Klasse Der Typ wird mit dem genauen Übereinstimmungsrang angegeben, und eine Konvertierung eines Ausdrucks des Klassentyps in eine Basisklasse dieses Typs erhält den Konvertierungsrang, obwohl ein Copy/Move-Konstruktor (dh eine benutzerdefinierte Konvertierungsfunktion) ein ist) wird für diese Fälle aufgerufen.

Für die Liste der Initialisierung Fall müssen Sie wahrscheinlich betrachten: 13.3.3.1.2 Benutzerdefinierte Konvertierungssequenzen [over.ics.user]

Wenn Objekte von Nicht-Aggregat-Klasse Typ T sind Liste initialisiert (8.5.4), wählt die Überladungsauflösung des Konstruktors in zwei Phasen:

- Zunächst werden die Funktionen der Initialisierer Kandidatenliste Konstruktoren (8.5.4) der Klasse T und die sind Die Argumentliste besteht aus der Initialisiererliste als Sing Das Argument.

- Wenn keine tragfähige Initialisierer-Liste Konstruktor gefunden wird, wird die Überladungsauflösung erneut durchgeführt, wobei die Kandidaten Funktionen sind alle Konstrukteure der Klasse T und die Argumentliste besteht aus den Elementen der Initialisiererliste.

Da die Überladungsauflösung bei tragfähige contructors jeweils suchen hat für f(A) und f(B) muss es ablehnen die seqence A() zu A(A&) aber B(const A&) noch lebensfähig ist zu binden versucht.

+0

Danke! Ich kann keine solche Regel für den Fall "{...}" finden. Erklärt das, warum der '{...}' Fall funktioniert? –

+0

@ JohannesSchaub-litb: Ich bin mir nicht sicher, tbh, du rufst die Funktion mit einer _bracked-init-Liste auf, also sind die Regeln definitiv anders. –

+0

@ JohannesSchaub-litb Siehe [over.ics.list]. Ich denke, das hat mit [over.ics zu tun.ref]/3 (Ich habe deinen Code früher falsch gelesen): Wenn man die Teilmenge von funktionsfähigen Funktionen bildet, wird der ctor 'A (A &)' nicht als brauchbar angesehen, da er eine temporäre zu einer nichtkonstanten lvalue-Referenz bindet. – dyp