2 Stimmen

Ungewöhnliches Verhalten bei Überladung mit Operator>=

Ich habe ein seltsames Verhalten mit einem Operator Überladen in C++. Ich habe eine Klasse, und ich muss prüfen, ob ihr Inhalt größer oder gleich einem langen Double ist. Ich habe den >=-Operator überladen, um diese Prüfung durchzuführen, meine Deklaration ist wie folgt:

bool MyClass::operator>=(long double value) const;

Ich muss sagen, dass ich auch einen cast-to-long-double-Operator für meine Klasse habe, der nur unter bestimmten Bedingungen ohne Ausnahmen funktioniert. Wenn ich nun diesen Operator verwende, beschwert sich der Compiler, dass es eine zweideutige Verwendung von operator>= gibt und die Alternativen sind:

  • Meine.
  • Die eingebaute operator>=(long double, int) .

Wie zwinge ich nun das Programm, meinen Operator zu verwenden?

3voto

Johannes Schaub - litb Punkte 479831

Aktualisierung 2015: Oder, wenn Sie die Konvertierungsfähigkeit behalten wollen, indem Sie die (double)obj Syntax stattdessen die obj.to_double() Syntax, machen Sie die Konvertierungsfunktion explicit durch Voranstellen dieses Schlüsselworts. Sie benötigen dann einen expliziten Cast, um die Konvertierung auszulösen. Ich persönlich bevorzuge die .to_double Syntax, es sei denn, die Umwandlung würde in bool denn in diesem Fall wird die Umwandlung von if(obj) auch wenn es sich explicit und das ist wesentlich besser lesbar als if(obj.to_bool()) meiner Meinung nach.


Lassen Sie den Konvertierungsoperator weg. Er wird auf dem ganzen Weg Probleme verursachen. Haben Sie eine Funktion wie

to_double()

Oder eine ähnliche Funktion, die den Double-Wert zurückgibt, und rufen Sie diese Funktion explizit auf, um einen Double-Wert zu erhalten.

Für das vorliegende Problem gibt es dieses Problem:

obj >= 10

Denken Sie an diesen Ausdruck. Der eingebaute Operator entspricht dem ersten Argument durch eine benutzerdefinierte Konvertierungssequenz für Ihren Typ unter Verwendung des Konvertierungsoperators long double(). Aber Ihre Funktion passt zum zweiten Argument durch eine Standardkonvertierungssequenz von int zu long double (Ganzzahl-zu-Gleitkomma-Konvertierung). Es ist immer zweideutig, wenn es Konvertierungen für zwei Argumente gibt, aber nicht mindestens ein Argument, das besser konvertiert werden kann, während die übrigen Argumente für einen Aufruf nicht schlechter konvertiert werden. In Ihrem Fall passt die eingebaute Funktion besser auf das zweite Argument, aber schlechter auf das erste, während Ihre Funktion besser auf das erste, aber schlechter auf das zweite Argument passt.

Das ist verwirrend, deshalb hier einige Beispiele (Umwandlungen von char in int werden Promotionen genannt, die besser sind als Umwandlungen von char in etwas anderes als int, was eine Konvertierung genannt wird):

void f(int, int);
void f(long, long);
f('a', 'a');

Ruft die erste Version auf. Denn alle Argumente für die erste können besser umgewandelt werden. Auch das Folgende ruft noch die erste auf:

void f(int, long);
void f(long, long);
f('a', 'a');

Denn die erste kann besser umgesetzt werden, und die zweite wird nicht schlechter umgesetzt. Aber das Folgende ist zweideutig :

void f(char, long);
void f(int, char);
f('a', 'a'); // ambiguous

In diesem Fall ist es noch interessanter. In der ersten Version wird das erste Argument durch eine exakte Übereinstimmung akzeptiert. Die zweite Version akzeptiert das zweite Argument durch eine exakte Übereinstimmung. Aber beide Versionen akzeptieren ihr anderes Argument nicht mindestens gleich gut. Die erste Version verlangt eine Konvertierung für ihr zweites Argument, während die zweite Version eine Beförderung für ihr Argument verlangt. Obwohl also eine Promotion besser ist als eine Konversion, schlägt der Aufruf der zweiten Version fehl.

Es ist sehr ähnlich wie in Ihrem Fall oben. Auch wenn eine Standardkonvertierungssequenz (Konvertierung von int/float/double nach long double) besser als eine benutzerdefinierte Konvertierungssequenz (Konvertierung von MyClass nach long double), wird Ihre Operatorversion nicht gewählt, weil Ihr anderer Parameter (long double) eine Konvertierung des Arguments erfordert, die schlechter ist als das, was der eingebaute Operator für dieses Argument benötigt (perfekte Übereinstimmung).

Die Auflösung von Überladungen ist in C++ eine komplexe Angelegenheit, so dass man sich unmöglich alle subtilen Regeln dazu merken kann. Aber einen groben Plan zu bekommen, ist durchaus möglich. Ich hoffe, es hilft Ihnen.

3voto

CB Bailey Punkte 693084

Durch eine implizite Umwandlung in eine double Sie sagen also, dass meine Klasse einer double und aus diesem Grund sollte es Sie nicht wirklich stören, wenn der eingebaute Operator >= für double s verwendet wird. Wenn Sie tun dann ist Ihre Klasse nicht wirklich "gleichwertig" mit einer double und Sie sollten sich überlegen, ob Sie nicht eine implizit Umstellung auf double sondern stattdessen die Bereitstellung eines ausdrücklich GetAsDouble, oder ConvertToDouble Mitgliedsfunktion.

Der Grund für die Zweideutigkeit im Moment ist, dass für einen Ausdruck t >= d donde t ist eine Instanz Ihrer Klasse und d ein Double ist, muss der Compiler immer entweder die linke oder die rechte Seite umwandeln, so dass der Ausdruck wirklich mehrdeutig ist. Entweder t 's operator double aufgerufen wird und der eingebaute Operator >= für double s verwendet wird, oder d muss zu a befördert werden long double und Ihr Mitgliedsoperator >= wird verwendet.

Bearbeiten, Sie haben Ihre Frage aktualisiert, um anzudeuten, dass Ihre Konvertierung in long double ist und Ihr Vergleich mit einem int ist. In diesem Fall sollte der letzte Absatz lauten:

Der Grund für die Zweideutigkeit im Moment ist, dass für einen Ausdruck t >= d donde t ist eine Instanz Ihrer Klasse und d ist ein int muss der Compiler immer entweder die linke oder die rechte Seite umwandeln, so dass der Ausdruck tatsächlich mehrdeutig ist. Entweder t 's operator long double aufgerufen wird und der eingebaute Operator >= für long double y int verwendet wird, oder d muss zu einem a befördert werden long double und Ihr Mitgliedsoperator >= wird verwendet.

1voto

dalle Punkte 17489

Ich nehme an, Sie vergleichen mit einer wörtlichen int und nicht ein long double :

MyClass o;

if (o >= 42)
{
   // ...
}

Wenn das der Fall ist, sind beide Alternativen gleich gut/komplex.

Mit Ihrem operator long double() :

  1. MyClass::operator long double()
  2. eingebaut operator>=(long double, int)

Mit Ihrem MyClass::operator>=(long double) :

  1. integrierte Konvertierung int a long double
  2. MyClass::operator>=(long double)

0voto

Daniel Earwicker Punkte 111630

Sie haben long double in der Erklärung. Versuchen Sie, es zu ändern in double .

0voto

Assaf Lavie Punkte 67504

Ihre Verwendung von Operatorüberladung in Kombination mit benutzerdefiniertem Casting kann für die Benutzer Ihrer Klasse sehr verwirrend sein. Fragen Sie sich, ob die Benutzer dieser Klasse erwarten sich in ein Double umwandeln oder mit einem Double vergleichbar sein? Würde eine Funktion .greaterThan(double) nicht das gleiche Ziel erreichen, ohne den Benutzer zu überraschen?

Ich schätze, Sie könnten Ihr Objekt vor dem Vergleich immer explizit in double umwandeln, um die Zweideutigkeit zu vermeiden. Aber wenn ich Sie wäre, würde ich den obigen Ansatz überdenken und mich darauf konzentrieren, Code zu schreiben, der intuitiv ist und sich auf nicht überraschende Weise verhält, anstelle von ausgefallenem Typ-Casting und Operator-Überladung.

(Inspiriert von der wunderbaren FQA Schimpfen über das Überladen von Operatoren )

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