Mehrdeutiger Operator
-
Und was wäre von dieser Implementierung der Operatoren zu halten. Ich traue es mich kaum den Code zu posten ;).
bool operator!=(const Double& value1, bool value2) { return (static_cast<double>(value1) != value2); } bool operator!=(const Double& value1, double value2) { return (static_cast<double>(value1) != value2); } bool operator!=(const Double& value1, float value2) { return (static_cast<double>(value1) != value2); } usw.
-
Ohne das gutheißen zu wollen und ohne genau zu wissen, wo Du hinwillst, kann man das aber auch mit Templates lösen:
template<typename T> bool operator==(const Double& d, const T& t) { return static_cast<double>(d) == t; }Und wie gesagt, mehr Code wär von Not.
-
Dein Problem wird dadurch ausgelöst, dass deine Doubleklasse einen Implizieten Konstruktor anbietet, der sie in eine normale Double umwandelt. Vermutlich hast auch einen Konstruktor, der eine double annimt. Da ist das Problem. Die impliziete Umwandung von Double in double muss weg! Mach eine Funktion draus!
-
Die erinnerung schrieb:
Dein Problem wird dadurch ausgelöst, dass deine Doubleklasse einen Implizieten Konstruktor anbietet, der sie in eine normale Double umwandelt. Vermutlich hast auch einen Konstruktor, der eine double annimt. Da ist das Problem. Die impliziete Umwandung von Double in double muss weg! Mach eine Funktion draus!
Das ist ein guter Tipp, habe jetzt testweise mal den implizieten Operator entfernt und eine Funktion angeboten die den normalen double zurückgibt. Diese Variante hat nur einen Haken. Ich kann jetzt z.B. keine Int32 mehr von einem Double subtrahieren, müsste also auch hier wieder sämtliche Operator-Kombinationen implementieren. Zudem hätte ich überall im Code diese GetValue-Funktion die den normalen double zurückgibt.
Eisflamme schrieb:
Ohne das gutheißen zu wollen und ohne genau zu wissen, wo Du hinwillst, kann man das aber auch mit Templates lösen:
template<typename T> bool operator==(const Double& d, const T& t) { return static_cast<double>(d) == t; }Das wäre schonmal eine Erleichterung.
-
Hat jemand eine Glaskugel übrig?
-
Das einfachste wäre ich könnte auf diese ganzen Operatoren verzichten und belasse den "implizieten" Operator. Allerdings habe ich da ja diese seltsame Fehlermeldung. Mir ist es sogar gelungen ein Minimalbeispiel zu stricken:
bool operator==(const B& a1, const B& a2); bool operator!=(const B& a1, const B& a2); class A { public: operator double() const; private: double value; }; A::operator double() const { return value; } class B { public: B(A a); //Das ist der Schlawiener void Test(); private: A a1; A a2; }; B::B(A a) { } void B::Test() { if (a1 == a2) // HIER KOMMT DER FEHLER { } } bool operator==(const B& a1, const B& a2) { return true; } bool operator!=(const B& a1, const B& a2) { return true; } int _tmain(int argc, _TCHAR* argv[]) { return 0; }Warum wird hier ein mehrdeutiger Operator gefunden?
-
MejrdeutigerOperator schrieb:
Warum wird hier ein mehrdeutiger Operator gefunden?
Weil die Konvertierung durch Konstruktor gleich gut/schlecht wie die mittels Konvertierungsoperator ist. Welche Variante willst du denn eigentlich?
Den eingebauten Operator oder den für B? Oder etwa einen für A, den du vergessen hast zu implementieren.
Du könntest auch den Konstruktor von B (oder auch den Konvertierunsgoperator in A mit C++11) explizit deklarieren. Ob das semantisch sinnvoll ist, lässt sich nat. bei Namne wie A und B kaum begründen.
-
Häh, ich vergleiche hier zwei Objekte der Klasse A das hat mit der Klasse B rein gar nichts zu tun. Warum der Compiler nun meint die A's in ein B umzuwandeln zu müssen ist mir ein Rätsel. Scheinbar sind Computer noch viel dümmer als ich angenommen hatte. Das einzig logische wäre die A's in einen double zu wandeln.
@camper
Nochmal hier gibt es keine Varianten. Klasse A und B hat rein gar nichts miteinander zu tun. Wozu gebe ich diesem bescheuerten Compiler im operator== dann überhaupt noch Datentypen an, wenn er sowieso alle Datentypen in betracht nimmt.
-
Um es nochmal so auszudrücken. Der Compiler merkt also, dass für die Klasse A gar kein operator== definiert wurde. Anstatt nun Klasse A dank des implementierten
operator double() const;Operators in einen double zu wandeln, nimmt er sich stattdessen den nächst besten operator== den er finden kann, auch wenn dieser für ganz andere Datentypen definiert wurde. Unglaublich.
-
Ich kann ja noch nachvollziehen, dass der Compiler aufgrund des Konstruktors der Klasse B der einen double entgegen nimmt auch diesen "Umwandlungsweg" in Betracht nimmt, aber er müsste dann doch merken, dass ein A nunmal kein B ist und dafür auch keine Umwandlung vorhanden ist und diesen "Umwandlungsweg" dann wieder verwerfen.
-

void B::Test() { if (a1 == a2) // HIER KOMMT DER FEHLER { } }Natürlich kommt da ein Fehler. Soll da nun
a1unda2implizite nachBkonvertiert und danach deineoperator==Funktion aufgerufen werden?a1unda2impliziet nachdoublekonvertiert und danach die built-inoperator==Funktion aufgerufen werden?
Diese Frage kannst nur dur beantworten, nicht ich nicht der Compiler und auch sonst niemand. Also sei so lieb und beantworte diese Frage dem Compiler.

Du kannst also entweder die implizite Konvertierung nach
Bverhindern, indem du den Konstruktorexplicitmachst. Oder aber du castest explizit nachdouble.
-
MehrdeutigerOperator schrieb:
Häh, ich vergleiche hier zwei Objekte der Klasse A das hat mit der Klasse B rein gar nichts zu tun. Warum der Compiler nun meint die A's in ein B umzuwandeln zu müssen ist mir ein Rätsel. Scheinbar sind Computer noch viel dümmer als ich angenommen hatte. Das einzig logische wäre die A's in einen double zu wandeln.
@camper
Nochmal hier gibt es keine Varianten. Klasse A und B hat rein gar nichts miteinander zu tun. Wozu gebe ich diesem bescheuerten Compiler im operator== dann überhaupt noch Datentypen an, wenn er sowieso alle Datentypen in betracht nimmt.class B { public: B(A a);Hiermit wird ein Konvertierungskonstruktor deklariert. Somit kann an (fast) jeder Stelle, an der eigentlich ein B erwartet wird, ein A verwendet werden.
Der Vergleichsoperator für B hat 2 B-Parameter, und kann dank dieses Konvertierungskonstruktors auch mit A-Argumenten aufgerufen werden.
Gleichermassen kann der eingebaute Vergleichsoperator für double eigentlich nur mit double-Argumenten arbeiten. Dank des deklarierten double-Konvertierungsoperators kann dieser Vergleichsoperator aber auch mit A-Argumenten gefüttert werden.Beide Optionen sind aus Sicht des Compilers gleichwertig, weil sie beide den Aufruf einer Konvertierungsfunktion erfordern.
Es gibt verschiedene Wege, diese Mehrdeutigkeit aufzulösen, allerdings keinen allgemingültigen, weil sie an anderer Stelle unterschiedliche Folgen haben. Konsequenterweise lässt sich mit Metasyntaktischen A und B kein guter Rat geben.
Die einzige Regel, die meist beachtet werden kann, ist es , implizite Konvertierungen nur Bedacht einzusetzen (was nicht weiterhilft, wenn sie eben doch mal gebraucht werden).
-
MehrdeutigerOperator schrieb:
Ich kann ja noch nachvollziehen, dass der Compiler aufgrund des Konstruktors der Klasse B der einen double entgegen nimmt auch diesen "Umwandlungsweg" in Betracht nimmt, aber er müsste dann doch merken, dass ein A nunmal kein B ist und dafür auch keine Umwandlung vorhanden ist und diesen "Umwandlungsweg" dann wieder verwerfen.
B hat keinen Konstruktor für double, nur einen für A.
-
[gg]
camper schrieb:
]B hat keinen Konstruktor für double, nur einen für A.
Da hast du du natürlich recht.
Wie würdet ihr denn eine solche Wrapper-Klasse, um einen Standard-Datenyp gestalten?
-
MehrdeutigerOperator schrieb:
[gg]
camper schrieb:
]B hat keinen Konstruktor für double, nur einen für A.
Da hast du du natürlich recht.
Wie würdet ihr denn eine solche Wrapper-Klasse, um einen Standard-Datenyp gestalten?Gar nicht?
Ohne jetzt den Thread zu erforschen, wofür brauchste das?
Ich würde einfach einen Konvertierungsoperator für eine Referenz und eine Kopie bereitstellen, und ein Internes Objekt halten.
-
Möglicherweise sowas:
template<typename Type> struct ScalarWrapper { static_assert(std::is_arithmetic<Type>::value, "Has to have scalar type!"); ScalarWrapper(Type const t) noexcept: mInst(t) {} constexpr ScalarWrapper() noexcept = default; operator Type&() noexcept {return mInst;} operator Type() const noexcept {return mInst;} private: Type mInst; };