Kurz
Die Konvertierungsfunktion operator int()
wird durch Klirren ausgewählt, operator bool() const
da sie b
nicht const-qualifiziert ist, wohingegen der Konvertierungsoperator für bool ist.
Die kurze Begründung ist, dass der Kandidat bei der Konvertierung b
in bool
are für die Überlastungsauflösung (mit impliziten Objektparametern) funktioniert
operator bool (B2 const &);
operator int (B2 &);
wo die zweite eine bessere Übereinstimmung ist, da b
nicht const qualifiziert ist.
Wenn beide Funktionen dieselbe Qualifikation haben (entweder beide const
oder nicht), operator bool
wird ausgewählt, da dies eine direkte Konvertierung ermöglicht.
Konvertierung per Cast-Notation, Schritt für Schritt analysiert
Wenn wir uns einig sind, dass der boolesche ostream-Inserter (std :: basic_ostream :: operator << (bool val) gemäß [ostream.inserters.arithmetic]) mit dem Wert aufgerufen wird, der sich aus einer Konvertierung von b
in bool
ergibt, können wir in diese Konvertierung graben .
1. Der Besetzungsausdruck
Die Besetzung von b zu bool
(bool)b
bewertet zu
static_cast<bool>(b)
gemäß C ++ 11, 5.4 / 4 [expr.cast], da const_cast
nicht anwendbar ist (hier keine const hinzufügen oder entfernen).
Diese statische Konvertierung ist gemäß C ++ 11, 5.2.9 / 4 [expr.static.cast] zulässig , wenn bool t(b);
für eine erfundene Variable t gut ausgebildet ist. Solche Anweisungen werden als Direktinitialisierung gemäß C ++ 11, 8.5 / 15 [dcl.init] bezeichnet .
2. Direkte Initialisierung bool t(b);
In Abschnitt 16 des am wenigsten erwähnten Standardabsatzes heißt es (Hervorhebung von mir):
Die Semantik der Initialisierer ist wie folgt. Der Zieltyp ist der Typ des zu initialisierenden Objekts oder der zu initialisierenden Referenz, und der Quelltyp ist der Typ des Initialisierungsausdrucks.
[...]
[...] Wenn der Quelltyp ein (möglicherweise lebenslaufqualifizierter) Klassentyp ist, werden Konvertierungsfunktionen berücksichtigt.
Die anwendbaren Konvertierungsfunktionen werden aufgelistet, und die beste wird durch Überlastungsauflösung ausgewählt.
2.1 Welche Konvertierungsfunktionen stehen zur Verfügung?
Die verfügbaren Konvertierungsfunktionen sind operator int ()
und operator bool() const
da wie C ++ 11, 12.3 / 5 [class.conv] sagt uns:
Eine Konvertierungsfunktion in einer abgeleiteten Klasse verbirgt eine Konvertierungsfunktion in einer Basisklasse nur, wenn die beiden Funktionen in denselben Typ konvertiert werden.
Während C ++ 11 in 13.3.1.5/1 [over.match.conv] Folgendes angibt:
Die Konvertierungsfunktionen von S und seinen Basisklassen werden berücksichtigt.
Dabei ist S die Klasse, aus der konvertiert wird.
2.2 Welche Konvertierungsfunktionen sind anwendbar?
C ++ 11, 13.3.1.5/1 [over.match.conv] (Hervorhebung von mir):
1 [...] Unter der Annahme, dass "cv1 T" der Typ des zu initialisierenden Objekts und "cv S" der Typ des Initialisierungsausdrucks ist, wobei S ein Klassentyp ist, werden die Kandidatenfunktionen wie folgt ausgewählt: Die Konvertierung Funktionen von S und seinen Basisklassen werden berücksichtigt. Diese nicht expliziten Konvertierungsfunktionen, die nicht in S verborgen sind und Typ T ergeben, oder ein Typ, der über eine Standardkonvertierungssequenz in Typ T konvertiert werden kann, sind Kandidatenfunktionen.
Daher operator bool () const
ist anwendbar, da es nicht in verborgen ist B2
und a ergibt bool
.
Der Teil mit dem Schwerpunkt im letzten Standardzitat ist für die Konvertierung relevant, operator int ()
da int
ein Typ ist, der über eine Standardkonvertierungssequenz in bool konvertiert werden kann. Die Konvertierung von int
nach bool
ist nicht einmal eine Sequenz, sondern eine einfache direkte Konvertierung, die gemäß C ++ 11, 4.12 / 1 [conv.bool] zulässig ist.
Ein Wert von arithmetischer, nicht skalierter Aufzählung, Zeiger oder Zeiger auf Elementtyp kann in einen Wert vom Typ bool konvertiert werden. Ein Nullwert, ein Nullzeigerwert oder ein Nullelementzeigerwert wird in false konvertiert. Jeder andere Wert wird in true konvertiert.
Dies bedeutet, dass dies auch operator int ()
anwendbar ist.
2.3 Welche Konvertierungsfunktion ist ausgewählt?
Die Auswahl der geeigneten Konvertierungsfunktion erfolgt über die Überlastungsauflösung ( C ++ 11, 13.3.1.5/1 [over.match.conv] ):
Die Überlastungsauflösung wird verwendet, um die aufzurufende Konvertierungsfunktion auszuwählen.
Es gibt eine besondere "Eigenart", wenn es um die Überlastungsauflösung für Klassenelementfunktionen geht: den impliziten Objektparameter ".
Per C ++ 11, 13.3.1 [over.match.funcs] ,
[...] sowohl statische als auch nicht statische Elementfunktionen haben einen impliziten Objektparameter [...]
Dabei lautet der Typ dieses Parameters für nicht statische Elementfunktionen gemäß Abschnitt 4:
"Wertreferenz auf Lebenslauf X" für Funktionen, die ohne Ref-Qualifier oder mit dem & Ref-Qualifier deklariert wurden
"R-Wert-Referenz auf Lebenslauf X" für Funktionen, die mit dem && ref-Qualifier deklariert wurden
Dabei ist X die Klasse, zu der die Funktion gehört, und cv die Lebenslaufqualifikation für die Deklaration der Mitgliedsfunktion.
Dies bedeutet, dass (gemäß C ++ 11, 13.3.1.5/2 [over.match.conv] ) bei einer Initialisierung durch Konvertierungsfunktion
Die Argumentliste enthält ein Argument, nämlich den Initialisiererausdruck. [Hinweis: Dieses Argument wird mit dem impliziten Objektparameter der Konvertierungsfunktionen verglichen. - Endnote]
Die Kandidatenfunktionen für die Überlastungsauflösung sind:
operator bool (B2 const &);
operator int (B2 &);
Offensichtlich operator int ()
ist eine bessere Übereinstimmung, wenn eine Konvertierung unter Verwendung eines nicht konstanten Objekts vom Typ angefordert wird, B2
da operator bool ()
eine Qualifizierungskonvertierung erforderlich ist.
Wenn beide Konvertierungsfunktionen dieselbe konstante Qualifikation haben, reicht die Überlastungsauflösung dieser Funktion nicht mehr aus. In diesem Fall wird das Conversion- (Sequenz-) Ranking durchgeführt.
3. Warum wird operator bool ()
ausgewählt, wenn beide Konvertierungsfunktionen dieselbe konstante Qualifikation haben?
Die Konvertierung von B2
nach bool
ist eine benutzerdefinierte Konvertierungssequenz ( C ++ 11, 13.3.3.1.2 / 1 [over.ics.user] ).
Eine benutzerdefinierte Konvertierungssequenz besteht aus einer anfänglichen Standardkonvertierungssequenz, gefolgt von einer benutzerdefinierten Konvertierung, gefolgt von einer zweiten Standardkonvertierungssequenz.
[...] Wenn die benutzerdefinierte Konvertierung durch eine Konvertierungsfunktion angegeben wird, konvertiert die anfängliche Standardkonvertierungssequenz den Quelltyp in den impliziten Objektparameter der Konvertierungsfunktion.
C ++ 11, 13.3.3.2/3 [over.ics.rank]
[...] definiert eine teilweise Anordnung impliziter Konvertierungssequenzen basierend auf den Beziehungen Bessere Konvertierungssequenz und bessere Konvertierung.
[...] Benutzerdefinierte Konvertierungssequenz U1 ist eine bessere Konvertierungssequenz als eine andere benutzerdefinierte Konvertierungssequenz U2, wenn sie dieselbe benutzerdefinierte Konvertierungsfunktion oder Konstruktor- oder Aggregatinitialisierung enthalten und die zweite Standardkonvertierungssequenz von U1 besser ist als die zweite Standardumwandlungssequenz von U2.
Die zweite Standardkonvertierung ist der Fall von operator bool()
is bool
to bool
(Identitätskonvertierung), während die zweite Standardkonvertierung bei operator int ()
is int
to bool
eine boolesche Konvertierung ist.
Daher ist die Konvertierungssequenz unter Verwendung operator bool ()
besser, wenn beide Konvertierungsfunktionen dieselbe konstante Qualifikation aufweisen.