Ich habe Kommentare oder Antworten zu SoF gesehen, die besagen, dass das Überladen des Darstellers zu bool gefährlich ist und ich stattdessen lieber void* operator
bevorzuge. Dennoch möchte ich fragen, ob es gefährlich ist, diesen Operator in meinem Anwendungsfall zu verwenden, und wenn ja, warum.
Ich implementiere eine einfache Geometrie-Bibliothek und eine ihrer grundlegendsten Klassen ist ein Punkt, den ich folgendermaßen definiere:
%Vor%Ich werde versuchen zu erklären, warum ich den Cast-Operator boolen muss. Ich habe eine andere Klasse namens line und ich habe eine Funktion wie folgt deklariert:
%Vor% Nachdem ich nun den Operator cast auf bool eines Punktes überladen habe, kann ich sowohl point p = intersect(a,b);
als auch if (intersect(a,b))
schreiben und so entweder den Schnittpunkt zweier Linien erhalten oder prüfen, ob sich zwei Linien schneiden. Ich benutze dies auch an anderen Stellen, aber ich glaube, dass dieses Beispiel ausreicht, um die Verwendung des Darstellers zu zeigen. Ist meine Verwendung des Cast-Operators gefährlich und wenn ja, könnten Sie bitte ein Beispiel geben, wenn der Effekt nicht wie erwartet ist?
EDIT: ein kleiner Zusatz dank juanchopanza: In diesem Projekt bin ich darauf beschränkt, c ++ 11 nicht zu verwenden, daher kann ich den Operator nicht explizit machen.
Das ist nicht gut genug. Da Sie mit C ++ 03 festgefahren sind, sollten Sie safe-bool idiom verwenden Verwenden Sie, wenn Sie so etwas überhaupt brauchen (andernfalls sollte explicit
conversion function bevorzugt werden).
Eine alternative Lösung besteht darin, stattdessen boost:optional
zurückzugeben:
Ich würde diesen Ansatz (sogar in C ++ 11) übernehmen, da er semantisch besser aussieht - parallele Linien haben keinen Schnittpunkt, daher sollte der Rückgabewert tatsächlich optional (oder < em> vielleicht Wert).
Ja, das ist gefährlich. Betrachten wir zum Beispiel Ihre point
genau wie sie ist, ohne Definition von operator+
. Trotz dieses Mangels, wenn wir versuchen, zwei point
-Objekte hinzuzufügen, wird der Compiler überhaupt kein Objekt:
Dies funktioniert, indem jedes point
nach bool
konvertiert wird und dann das bool
s hinzugefügt wird. In einem Integer-Kontext wird false
in 0 konvertiert und true
in 1 konvertiert. Daher können wir erwarten, dass der obige Code 2 ausgibt. Natürlich habe ich gerade die Addition als Beispiel verwendet. Sie können genauso gut Subtraktion, Multiplikation, Division, bitweise Operationen, logische Operationen usw. durchführen. Sie alle werden kompilieren und ausführen, aber bringen offensichtlich wertlose Ergebnisse. Wenn wir eine point
an eine Funktion übergeben, die nur einen numerischen Typ akzeptiert, stoppt der Compiler nicht s oder meckert - er konvertiert nur in bool
, und die Funktion erhält entweder 0
oder 1
abhängig davon, ob real
wahr war oder nicht.
Das Safe-Bool-Idiom ist sicher (na ja, weniger gefährlich), da Sie void *
in einem Booleschen Kontext testen können, aber ein void *
wird nicht implizit in etwas anderes konvertiert, wie bool
. Sie (meistens 1 ) können nicht zufällig arithmetische, bitweise Operationen usw. auf ihnen ausführen.
1. Es gibt sowieso ein paar Löcher, meistens mit dem Aufruf von etwas anderem, das eine Art explizite Konvertierung für void *
durchführt. Die Möglichkeit, Konvertierungsoperatoren explicit
zu markieren, ist besser, aber ehrlich gesagt, sie verbessern die Lesbarkeit wesentlich mehr als die Sicherheit.
Seltsam, dass diese Leute Ihnen nicht gesagt haben, warum das Überladen von operator bool()
gefährlich ist.
Der Grund dafür ist, dass C ++ implizit von bool
in alle anderen numerischen Typen konvertiert wird. Also, wenn Sie operator bool()
überladen, dann verlieren Sie eine Menge von Typ-Checks, die Benutzer der Klasse normalerweise erwarten würden. Sie können point
überall dort angeben, wo ein int
oder float
erwartet wird.
Das Überladen von operator void*()
ist weniger gefährlich, da weniger void*
in konvertiert wird, aber immer noch das gleiche grundlegende Problem hat, dass der void*
-Typ für andere Dinge verwendet wird.
Die Idee des sicheren Bool-Idioms besteht darin, einen Zeigertyp zurückzugeben, der nicht zu irgendetwas konvertiert, das in einer API verwendet wird.
Beachten Sie, dass Sie sich darüber keine Gedanken machen müssen, wenn Sie if (intersect(a,b).real)
oder vielleicht if (intersect(a,b).exists())
schreiben möchten. C ++ 03 hat explizite Konvertierungen. Jede Funktion mit einem Parameter (oder einer nicht statischen Elementfunktion ohne Parameter) ist eine Art von Konvertierung. C ++ 03 hat keine explizite Konvertierung Operatoren .
Aufgrund von impliziten Konvertierungen von bool in einen numerischen Wert könnte es zu Problemen kommen. Außerdem hat Ihre Zeilenklasse ein (in meinem Punkt) gefährliches Mitglied 'bool real' bekommen, um Ergebnisse von Funktionsaufrufen zu tragen. Eine Schnittpunktfunktion, die einen Schnittpunktwert oder NaN zurückgibt, wenn die Linien sich nicht überschneiden, könnte Ihr Problem lösen.
Beispiel
Eine (unendliche) Zeilenklasse mit einem Ursprung p und einem Vektor v:
%Vor%Und eine Funktion, die einen Schnittpunktwert berechnet
%Vor%Dann können Sie tun:
%Vor%Oder
%Vor%Zum Beispiel hatte std :: basic_ios zu Beginn der C ++ 2003 Standard-Klasse den folgenden Konvertierungsoperator
%Vor%Jetzt, da der neue C ++ Standard ein Schlüsselwort explizit hat, wurde dieser Operator durch
ersetzt %Vor%Wenn Sie also ein Keyword explizit für Ihren Operator hinzufügen, denke ich, dass es nicht gefährlich ist.:
Die andere Möglichkeit ist, den Operator bool operator! () anstelle des Konvertierungsoperators zu definieren.
Obwohl es bereits einige sehr gute Antworten gibt, möchte ich eine etwas vollständigere Antwort zusammen mit der Erklärung, warum die vorgeschlagene Lösung funktioniert, zusammenstellen.
1. Warum ist meine Lösung falsch? Der Compiler führt implizite Konvertierungen von bool in numerische Typen durch, wodurch unerwartete Operatoren definiert werden. Zum Beispiel:
%Vor% Kompiliert, obwohl ich es nicht erwartet habe. Ändern des Operators für die Konvertierung in operator void*()
wird die Dinge aber nur geringfügig verbessern, da einige Operatoren (obwohl weniger als für bool), die für void *
definiert sind, und ihre Existenz wieder unerwartetes Verhalten zur Folge haben werden.
2.Was ist die richtige Lösung? Die richtige Lösung finden Sie hier . Aber ich werde den Code für meine Klasse einfügen, damit ich erklären kann, warum das funktioniert und warum wir das Problem auf diese Weise lösen:
%Vor% Nun erkläre ich zuerst, warum wir einen Typ verwenden müssen, der ein Funktionszeiger und kein einfacher Zeiger ist. Die Antwort wird von Steve Jessop in einem Kommentar unter Nawaz 'Antwort gegeben. function pointers are not < comparable
, daher wird jeder Versuch, p < 5
oder irgendeinen anderen Vergleich zwischen einem Punkt und einem anderen Typ zu schreiben, nicht kompiliert.
Warum brauchen wir eine Methode ohne Körper ( not_supported
)? Weil wir auf diese Weise jedes Mal, wenn wir versuchen, die vorbereiteten Methoden für den Vergleich zu instanziieren (das heißt ==
und !=
), einen Funktionsaufruf einer Methode ohne Körper haben, der einen Kompilierungsfehler verursacht.
Tags und Links c++ casting operator-overloading boolean