Betrachten Sie das folgende kurze C ++ - Programm:
#include <iostream>
class B {
public:
operator bool() const {
return false;
}
};
class B2 : public B {
public:
operator int() {
return 5;
}
};
int main() {
B2 b;
std::cout << std::boolalpha << (bool)b << std::endl;
}
Wenn ich es auf verschiedenen Compilern kompiliere, erhalte ich verschiedene Ergebnisse. Mit Clang 3.4 und GCC 4.4.7 wird gedruckt true
, während Visual Studio 2013 druckt false
, was bedeutet, dass verschiedene Besetzungsoperatoren unter aufgerufen werden (bool)b
. Welches ist das richtige Verhalten gemäß dem Standard?
In meinem Verständnis operator bool()
keine Konvertierung benötigt, während operator int()
wäre eine erfordern , int
um bool
Umwandlung, so dass der Compiler die ersten wählen soll. Hat const
etwas damit zu tun, wird die Konstantenkonvertierung vom Compiler als "teurer" angesehen?
Wenn ich das entferne const
, produzieren alle Compiler gleichermaßen false
als Ausgabe. Wenn ich dagegen die beiden Klassen miteinander kombiniere (beide Operatoren befinden sich in derselben Klasse), erzeugen alle drei Compiler eine true
Ausgabe.
quelle
const
ist der Schlüssel hier. Da beide Operatoren gleich konstant sind oder nicht, verhalten sie sich wie erwartet, diebool
Version "gewinnt". Bei unterschiedlicher Konstanz "gewinnt" immer die Nicht-Konstanten-Version auf jeder Plattform. Mit abgeleiteten Klassen und unterschiedlicher Konstanz der Operatoren scheint VS einen Fehler zu haben, da es sich anders verhält als die beiden anderen Compiler.true
. Wenn GCC, Clang und EDG übereinstimmen und MSVC nicht einverstanden sind, bedeutet dies normalerweise, dass MSVC falsch ist.Antworten:
Der Standard besagt:
Was bedeutet, dass
operator bool
das nicht verborgen istoperator int
.Der Standard besagt:
Das "implizite Objektargument" ist in diesem Fall
b
vom TypB2 &
.operator bool
erfordertconst B2 &
, so dass der Compiler const hinzufügen muss,b
um aufzurufenoperator bool
. Dies - alles andere ist gleich - passtoperator int
besser zusammen.Der Standard besagt, dass a
static_cast
(das der Cast im C-Stil in diesem Fall ausführt) in einen Typ konvertiert werden kannT
(in diesem Fallint
), wenn:Daher
int
kann das in a umgewandelt werdenbool
, und abool
kann gleichermaßen in a umgewandelt werdenbool
.Der Standard besagt:
Der Überlastsatz besteht also aus
operator int
undoperator bool
.operator int
Wenn alle anderen Dinge gleich sind, ist dies eine bessere Übereinstimmung (da Sie keine Konstanz hinzufügen müssen). Daheroperator int
sollte ausgewählt werden.Beachten Sie, dass der Standard (möglicherweise gegen die Intuition) den Rückgabetyp (dh den Typ, in den diese Operatoren konvertieren) nicht berücksichtigt, sobald sie dem Überlastungssatz hinzugefügt wurden (wie oben festgelegt), vorausgesetzt, die Konvertierungssequenz für die Argumente eines von Sie sind der Konvertierungssequenz für die Argumente des anderen überlegen (was in diesem Fall aufgrund der Konstanz der Fall ist).
Der Standard besagt:
In diesem Fall gibt es nur ein Argument (den impliziten
this
Parameter). Die Umwandlungsfolge fürB2 &
=>B2 &
(to Calloperator int
) überlegen istB2 &
=>const B2 &
(to Calloperator bool
) und daheroperator int
von dem Überlastsatz , ohne Rücksicht auf die Tatsache ausgewählt wird , dass sie eigentlich nicht umwandeln nicht direkt anbool
.quelle
Kurz
Die Konvertierungsfunktion
operator int()
wird durch Klirren ausgewählt,operator bool() const
da sieb
nicht const-qualifiziert ist, wohingegen der Konvertierungsoperator für bool ist.Die kurze Begründung ist, dass der Kandidat bei der Konvertierung
b
inbool
are für die Überlastungsauflösung (mit impliziten Objektparametern) funktioniertoperator 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
inbool
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):
2.1 Welche Konvertierungsfunktionen stehen zur Verfügung?
Die verfügbaren Konvertierungsfunktionen sind
operator int ()
undoperator bool() const
da wie C ++ 11, 12.3 / 5 [class.conv] sagt uns:Während C ++ 11 in 13.3.1.5/1 [over.match.conv] Folgendes angibt:
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):
Daher
operator bool () const
ist anwendbar, da es nicht in verborgen istB2
und a ergibtbool
.Der Teil mit dem Schwerpunkt im letzten Standardzitat ist für die Konvertierung relevant,
operator int ()
daint
ein Typ ist, der über eine Standardkonvertierungssequenz in bool konvertiert werden kann. Die Konvertierung vonint
nachbool
ist nicht einmal eine Sequenz, sondern eine einfache direkte Konvertierung, die gemäß C ++ 11, 4.12 / 1 [conv.bool] zulässig ist.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] ):
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] ,
Dabei lautet der Typ dieses Parameters für nicht statische Elementfunktionen gemäß Abschnitt 4:
Dies bedeutet, dass (gemäß C ++ 11, 13.3.1.5/2 [over.match.conv] ) bei einer Initialisierung durch Konvertierungsfunktion
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
daoperator 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
nachbool
ist eine benutzerdefinierte Konvertierungssequenz ( C ++ 11, 13.3.3.1.2 / 1 [over.ics.user] ).C ++ 11, 13.3.3.2/3 [over.ics.rank]
Die zweite Standardkonvertierung ist der Fall von
operator bool()
isbool
tobool
(Identitätskonvertierung), während die zweite Standardkonvertierung beioperator int ()
isint
tobool
eine boolesche Konvertierung ist.Daher ist die Konvertierungssequenz unter Verwendung
operator bool ()
besser, wenn beide Konvertierungsfunktionen dieselbe konstante Qualifikation aufweisen.quelle
a/b
ist gleich, ob der Code istfloat f = a/b;
oderint f = a/b;
. Dies ist jedoch ein Fall, in dem es etwas überraschend sein kann.Der C ++ - Bool-Typ hat zwei Werte - true und false mit den entsprechenden Werten 1 und 0. Die inhärente Verwirrung kann vermieden werden, wenn Sie einen Bool-Operator in der B2-Klasse hinzufügen, der den Bool-Operator der Basisklasse (B) explizit aufruft. Dann kommt die Ausgabe als falsch. Hier ist mein modifiziertes Programm. Dann bedeutet Operator Bool Operator Bool und keinesfalls Operator Int.
#include <iostream> class B { public: operator bool() const { return false; } }; class B2 : public B { public: operator int() { return 5; } operator bool() { return B::operator bool(); } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; }
In Ihrem Beispiel hat (bool) b versucht, den bool-Operator für B2 aufzurufen, B2 hat den bool-Operator geerbt, und der int-Operator wird nach der Dominanzregel der int-Operator und der geerbte bool-Operator in B2 aufgerufen. Durch das explizite Vorhandensein eines Bool-Operators in der B2-Klasse selbst wird das Problem jedoch gelöst.
quelle
true and false with corresponding values 1 and 0
Das ist eine Vereinfachung.true
konvertiert in eine Ganzzahl1
, aber das bedeutet nicht, dass es den Wert "hat"1
. In der Tattrue
kann42
darunter sein.bool
nie den Wert 0 oder 1; Die einzigen Werte, die es annehmen kann, sindfalse
undtrue
(die in 0 und 1bool
konvertiert werden, wenn das in einen integralen Typ konvertiert wird).Einige der vorherigen Antworten enthalten bereits viele Informationen.
Mein Beitrag ist, dass "Cast-Operationen" ähnlich wie "überladene Operationen" kompiliert werden. Ich schlage vor, für jede Operation eine Funktion mit einer eindeutigen Kennung zu erstellen und diese später durch den erforderlichen Operator oder Cast zu ersetzen.
#include <iostream> class B { public: bool ToBool() const { return false; } }; class B2 : public B { public: int ToInt() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << b.ToBool() << std::endl; }
Wenden Sie später den Operator oder die Besetzung an.
#include <iostream> class B { public: operator bool() { return false; } }; class B2 : public B { public: operator int() { return 5; } }; int main() { B2 b; std::cout << std::boolalpha << (bool)b << std::endl; }
Nur meine 2 Cent.
quelle