3 Stimmen

Unterscheidung zwischen benutzerdefinierten Konvertierungssequenzen durch die ursprüngliche Standardkonvertierungssequenz

Der Standard scheint zwei Regeln zur Unterscheidung zwischen impliziten Konvertierungssequenzen, die benutzerdefinierte Konvertierungsoperatoren beinhalten, vorzusehen:

c++11

13.3.3 Beste brauchbare Funktion [over.match.best]

[...] eine lebensfähige Funktion F1 wird als bessere Funktion bezeichnet als F2, wenn [...]

  • der Kontext ist eine Initialisierung durch benutzerdefinierte Konvertierung (siehe 8.5, 13.3.1.5 und 13.3.1.6) und die Standardkonvertierungssequenz vom Rückgabetyp von F1 zum Zieltyp (d.h. dem Typ der Entität, die initialisiert wird) eine bessere Konvertierungssequenz ist als die Standardkonvertierungssequenz vom dem Rückgabetyp von F2 in den Zieltyp.

13.3.3.2 Rangfolge impliziter Konvertierungssequenzen [over.ics.rank]

3 - Zwei implizite Konvertierungssequenzen der gleichen Form sind ununterscheidbare Konvertierungssequenzen der folgenden Regeln gilt: [...]

  • Die benutzerdefinierte Konvertierungssequenz U1 ist eine bessere Konvertierungssequenz als eine andere benutzerdefinierte Konvertierungssequenz U2, wenn sie die gleiche benutzerdefinierte Konvertierungsfunktion oder den gleichen Konstruktor oder das gleiche Aggregat enthält Initialisierung enthalten und die zweite Standardkonvertierungsfolge von U1 besser ist als die zweite Standard Konvertierungssequenz von U2.

So wie ich es verstehe, erlaubt 13.3.3 dem Compiler zu unterscheiden zwischen verschiedene benutzerdefinierte Umwandlungsoperatoren , während 13.3.3.2 dem Compiler erlaubt, zu unterscheiden zwischen verschiedene Funktionen (Überladungen einer Funktion f ), die jeweils eine benutzerdefinierte Konvertierung in ihren Argumenten erfordern (siehe meine Seitenleiste zu Warum wird bei folgendem Code (in GCC 4.3) die Umwandlung in eine Referenz zweimal aufgerufen? ).

Gibt es noch andere Regeln, die zwischen benutzerdefinierten Konvertierungssequenzen unterscheiden können? Die Antwort unter https://stackoverflow.com/a/1384044/567292 weist darauf hin, dass 13.3.3.2:3 zwischen benutzerdefinierten Konvertierungssequenzen auf der Grundlage der cv-Qualifikation des impliziten Objektparameters (für einen Konvertierungsoperator) oder des einzelnen Nicht-Standardparameters für einen Konstruktor oder eine Aggregatinitialisierung unterscheiden kann, aber ich sehe nicht, wie das relevant sein kann, da dies einen Vergleich zwischen den ersten Standardkonvertierungssequenzen der jeweiligen benutzerdefinierten Konvertierungssequenzen erfordern würde, was der Standard nicht zu erwähnen scheint.

Angenommen, S1 ist besser als S2, wobei S1 die erste Standardkonvertierungssequenz von U1 und S2 die erste Standardkonvertierungssequenz von U2 ist, folgt daraus, dass U1 besser ist als U2? Mit anderen Worten: Ist dieser Code wohlgeformt?

struct A {
    operator int();
    operator char() const;
} a;
void foo(double);
int main() {
    foo(a);
}

g++ (4.5.1), Clang (3,0) und Comeau (4.3.10.1) akzeptieren es und bevorzugen die nicht-konst-qualifizierte A::operator int() aber ich würde erwarten, dass er als zweideutig und damit als unvollständig abgelehnt wird. Ist dies ein Mangel in der Norm oder in meinem Verständnis der Norm?

6voto

aschepler Punkte 68538

Der Trick dabei ist, dass die Konvertierung von einem Klassentyp in einen Nicht-Klassentyp keine benutzerdefinierten Konvertierungen als implizite Konvertierungssequenzen einstuft.

struct A {
    operator int();
    operator char() const;
} a;
void foo(double);
int main() {
    foo(a);
}

In dem Ausdruck foo(a) , foo benennt offensichtlich eine nicht überladene Nicht-Mitglieder-Funktion. Der Aufruf erfordert eine Kopierinitialisierung (8.5p14) des Funktionsparameters vom Typ double unter Verwendung des einzigen Ausdrucks a der ein l-Wert der Klasse type ist A .

Da der Zieltyp double ist kein cv-qualifizierter Klassentyp, sondern der Quelltyp A ist, werden die Kandidatenfunktionen gemäß Abschnitt 13.3.1.5 definiert, wobei S=A y T=double . Nur Konvertierungsfunktionen der Klasse A und alle Basisklassen von A berücksichtigt werden. Eine Konvertierungsfunktion ist in der Menge der Kandidaten, wenn:

  • Es ist nicht in der Klasse versteckt A und
  • Es ist nicht explicit (da der Kontext die Kopierinitialisierung ist), und
  • Eine Standardkonvertierungssequenz kann den Rückgabetyp der Funktion (ohne Referenzqualifizierer) in den Zieltyp konvertieren double .

Okay, beide Konvertierungsfunktionen sind qualifiziert, also sind die Kandidatenfunktionen

A::operator int();        // F1
A::operator char() const; // F2

Unter Anwendung der Regeln aus 13.3.1p4 hat jede Funktion den impliziten Objektparameter als einzigen in ihrer Parameterliste. F1 ist die Parameterliste "(lWert-Referenz auf A )" und F2 ist die Parameterliste "(lWert-Referenz auf const A )".

Als nächstes prüfen wir, ob die Funktionen lebensfähig sind (13.3.2). Jede Funktion hat einen Typ in ihrer Parameterliste, und es gibt ein Argument. Gibt es für jedes Argument/Parameterpaar eine implizite Konvertierungssequenz? Ja, natürlich:

  • ICS1(F1) : Binden Sie den impliziten Objektparameter (Typ lvalue-Referenz auf A ) zum Ausdruck a (lWert vom Typ A ).
  • ICS1(F2) : Binden Sie den impliziten Objektparameter (Typ lvalue-Referenz auf const A ) zum Ausdruck a (lWert vom Typ A ).

Da keine Konvertierung von abgeleitet zu Basis stattfindet, werden diese Referenzbindungen als Spezialfälle der Identitätskonvertierung betrachtet (13.3.3.1.4p1). Ja, beide Funktionen sind praktikabel.

Nun müssen wir feststellen, ob eine implizite Umrechnungssequenz besser ist als die andere. Dies fällt unter den fünften Unterpunkt der großen Liste in 13.3.3.2p3: Beide sind Referenzbindungen an denselben Typ, mit Ausnahme von cv-Qualifiern der obersten Ebene. Da der referenzierte Typ für ICS1(F2) mehr cv-qualifiziert ist als der referenzierte Typ für ICS1(F1) , ICS1(F1) ist besser als ICS1(F2) .

Deshalb F1 或いは A::operator int() ist die praktikabelste Funktion. Und es sollten auch keine benutzerdefinierten Konvertierungen (mit der strengen Definition eines Typs von ICS, bestehend aus SCS + (konvertierender Konstruktor oder Konvertierungsfunktion) + SCS) verglichen werden.

Wenn nun foo überladen wurden, benutzerdefinierte Konvertierungen auf das Argument a verglichen werden müssen. Dann wird die benutzerdefinierte Konvertierung (Identität + A::operator int() + int a double ) mit anderen impliziten Konvertierungssequenzen verglichen werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X