C ++ implizite Konvertierungen mit geschweiften Initialisierern

8

Ich habe kürzlich irgendwo gelesen (kann mich nicht erinnern, wo), geschweifte Klammern zu verwenden, um mehrere benutzerdefinierte Konvertierungen zu erlauben, aber es scheint einen Unterschied zwischen Konversion durch Konstruktor und Konvertierung durch Konvertierungsmethode zu geben, die ich nicht verstehe.

Überlegen Sie:

%Vor%

Nun, ich interpretiere vielleicht falsch, was der Compiler macht, aber (basierend auf den obigen und zusätzlichen Experimenten) scheint es mir, dass es keine Conversions nach der Conversion-Methode in Betracht zieht. In den Abschnitten 4 und 13.3.3.1 des Standards konnte ich jedoch keine Anhaltspunkte finden, warum dies so ist. Was ist die Erklärung?

Aktualisieren

Hier ist ein weiteres interessantes Phänomen, das ich gerne erklärt hätte. Wenn ich

hinzufüge %Vor%

und in main :

%Vor%

Ich bekomme einen Fehler, aber wenn ich stattdessen d.operator<<({{ "char *" }}); schreibe funktioniert es gut.

Update 2

Sieht so aus, als würde Abschnitt 8.5.4 im Standard einige Antworten enthalten. Ich werde meine Ergebnisse melden.

    
Ari 27.05.2016, 11:40
quelle

2 Antworten

5

Es ist eine Benutzerkonvertierung möglich.

In b = {{"char *"}};

machen wir eigentlich

%Vor%

so

%Vor%

in c = {{"const char*"}} , wir versuchen

%Vor%     
Jarod42 27.05.2016 11:59
quelle
0

Wenn ich durch Abschnitt 8.5.4 des Standards gehe und verschiedene Querverweise darin befolge, glaube ich zu verstehen, was vor sich geht. Natürlich, IANAL, also könnte ich mich irren. Das ist meine beste Leistung.

Aktualisierung: Die vorherige Version der Antwort verwendete tatsächlich mehrere Conversions. Ich habe es aktualisiert, um mein aktuelles Verständnis widerzuspiegeln.

Der Schlüssel zum Aufdecken der Unordnung ist die Tatsache, dass eine starre-init-Liste nicht ein Ausdruck ist (was auch erklärt, warum d << {{"char *"}} nicht kompiliert wird). Was es ist, ist eine spezielle Syntax, die durch spezielle Regeln geregelt wird, die in einer Reihe spezifischer Kontexte erlaubt ist. Von diesen Kontexten sind die relevanten für unsere Diskussion: rhs der Zuweisung, Argument in einem Funktionsaufruf und Argument in einem Konstruktoraufruf.

Was passiert also, wenn der Compiler b = {{"char *"}} sieht? Dies ist ein Fall von rhs Zuweisung. Die anwendbare Regel ist:

  

Eine braced-init-Liste kann auf der rechten Seite von ... einer Zuweisung erscheinen, die von einem benutzerdefinierten Zuweisungsoperator definiert wird. In diesem Fall wird die Initialisierungsliste übergeben   als Argument für die Operatorfunktion.

(Vermutlich wird der Standardkopiezuweisungsoperator als benutzerdefinierter Zuweisungsoperator betrachtet. Ich konnte nirgendwo eine Definition dieses Begriffs finden, und es scheint keine Sprache zu geben, die dies zulässt die geschweifte Syntax speziell für die Standardkopiezuweisung.)

Wir reduzieren uns also auf die Übergabe des Arguments an den Standard-Kopierzuweisungsoperator B::operator=(const B&) , wobei das übergebene Argument {{"char *"}} ist. Da eine brained-init-Liste kein Ausdruck ist, gibt es hier kein Konvertierungsproblem, sondern eine Form der Initialisierung eines temporären Typs B , insbesondere der so genannten Listeninitialisierung .

  

Wenn kein ausführbarer Initialisierungslistenkonstruktor gefunden wird, wird die Überladungsauflösung erneut ausgeführt, wobei der   Kandidatenfunktionen sind alle Konstruktoren der Klasse T und die Argumentliste besteht aus den Elementen   der Initialisierungsliste.

Der Compiler entfernt also das äußere Klammerpaar und führt die Überladungsauflösung mit {"char *"} als Argument durch. Dies ist passend zum Konstruktor B::B(const A&) , da es erneut eine Listeninitialisierung eines temporären Typs A gibt, in der die Überladungsauflösung A::A(const string&) für das Argument "char *" entspricht, was durch den benutzerdefinierten Benutzer möglich ist Umwandlung, nämlich von char* nach string .

Nun, im Fall von c = {{"char *"}} ist der Prozess ähnlich, aber wenn wir versuchen, ein temporäres vom Typ C mit {{"char *"}} aufzulisten, kann die Überladungsauflösung keinen passenden Konstruktor finden. Der Punkt ist, dass per Definition die list-Initialisierung nur über einen Konstruktor funktioniert, dessen Parameterliste so erstellt werden kann, dass sie mit dem Inhalt der Liste übereinstimmt.

    
Ari 28.05.2016 18:50
quelle