virtual operator== problem
-
hallo zusammen!
ich habe ein problem mit virtueller vererbung.class Base { public: Base(){} virtual bool operator==(const Base &other){ return baseInt == other.baseInt; } private: int baseInt = 1; }; class A : virtual public Base { public: A(int _a) : aInt(_a){} bool operator==(const Base &other) override { return Base::operator==(other) && aInt == static_cast<A*>(*other); // geht nicht } private: int aInt = 2; }; Base *a1 = new A(5); Base *a2 = new A(6); bool test = *a1 == *a2;wie kann ich aInt vergleichen?
-
class Base { public: Base(){} bool operator==(const Base &other){ return compare(other) && other.compare(*this); } protected: virtual bool compare(const Base &other) const { return baseInt == other.baseInt; } private: int baseInt = 1; }; class A : virtual public Base { public: A(int _a) : aInt(_a){} protected: bool compare(const Base &other) const override { auto *o = dynamic_cast<const A*>(&other); return o && Base::compare(other) && aInt == o->aInt; } private: int aInt = 2; }; auto a1 = std::make_unique<A>(5); auto a2 = std::make_unique<A>(6); bool test = *a1 == *a2;
-
Korrektur: Da muss noch ein
virtual ~Base(){}rein und unten sollte es heissen
std::unique_ptr<Base> a1 = std::make_unique<A>(5); std::unique_ptr<Base> a2 = std::make_unique<A>(6);
-
wow, das war schnell, danke!
das wirkt recht kompliziert, ginge es auch einfacher?
-
scheint, als ob
auto *o = dynamic_cast<const A*>(&other);reichen würde und der Base::compare nicht nötig wäre.
-
Operatorüberladung + Polymorphie? Für gewöhnlich keine gute Idee.
-
Hä, Operatoren können echt virtuell sein? Hab ich noch nie gesehen.
-
Mechanics schrieb:
Hä, Operatoren können echt virtuell sein? Hab ich noch nie gesehen.
Natürlich. Sind lediglich Funktionen mit sonderlichen Namen und Aufruf via Infix-Notation. Dieser Wolf-Spinner hat sogar ein Kapitel (4.8.5) über polymorphe Zuweisungsoperatoren in seinem berüchtigten Erguss erstellt.
-
Arcoth schrieb:
Natürlich.
Ist mir echt noch nie untergekommen. Wenn man nach polymorphic operators sucht, findet man eher sowas:
http://www.drdobbs.com/cpp/simulating-polymorphic-operators-in-c/200001978
Da gehts zwar (zumindest auf der ersten Seite, hab nicht alles durchgeschaut) um Operatoren, die als freie Funktionen implementiert sind, aber das ganze vermittelt doch den Eindruck, als ob das nicht direkt mit virtual gehen würde.
Ok, hat jemand irgendwelche Erfahrungen damit, was spricht konkret dagegen? Ein Vergleichsoperator ist wahrscheinlich nicht unproblematisch, man bräuchte fast double dispatch, sonst ergibt a == b evtl. was anderes als b == a. Allerdings hat man dasselbe Problem, wenn man eine equals Funktion einbaut, das machts ja nicht besser.
-
Arcoth schrieb:
Mechanics schrieb:
Hä, Operatoren können echt virtuell sein? Hab ich noch nie gesehen.
Natürlich. Sind lediglich Funktionen mit sonderlichen Namen und Aufruf via Infix-Notation. Dieser Wolf-Spinner hat sogar ein Kapitel (4.8.5) über polymorphe Zuweisungsoperatoren in seinem berüchtigten Erguss erstellt.
Ach, Du hast den Wolf gelesen
.
-
Mechanics schrieb:
was spricht konkret dagegen?
dagegen spricht, dass man zum überladen dieselbe signatur benutzen und dann dynamisch casten muss. is nicht so elegant, funktioniert aber:
class Base { public: Base(){} virtual bool operator==(const Base &other) { return baseInt == other.baseInt; } Base(const Base &other){ baseInt = other.baseInt; } private: int baseInt = 1; }; class A : virtual public Base { public: A(int _a) : aInt(_a){} virtual bool operator==(const Base &other) override { auto *o = dynamic_cast<const A*>(&other); return Base::operator==(other) && o && aInt == o->aInt; } A(const A &other) : Base(other){ aInt = other.aInt; } private: int aInt = 2; }; class A2 : virtual public Base { public: A2(int _a) : a2Int(_a){} virtual bool operator==(const Base &other) override { auto *o = dynamic_cast<const A2*>(&other); return Base::operator==(other) && o && a2Int == o->a2Int; } A2(const A2 &other) : Base(other){ a2Int = other.a2Int; } private: int a2Int = 2; }; class B : public A, public A2 { public: B(int _b) : bInt(_b), A(_b * 2), A2(_b*3){} bool operator==(const Base &other){ auto b = dynamic_cast<const B*>(&other); return A::operator==(other) && A2::operator==(other) && b && b->bInt == bInt; } B(const B &other) : Base(other), A(other), A2(other){ bInt = other.bInt; } private: int bInt = 3; };und in diesem fall wird Base::operator== doppelt aufgerufen.
und beim hinzufügen weiterer klassen darf man das nicht vergessen...
-
mael15 schrieb:
...
Wichtig ist, dass der Vergleichsoperator symmetrisch ist (a==b <=> b==a). Die beste Lösung dazu ist, zwei Vergleiche durchzuführen, wie ich das gezeigt habe:
return compare(other) && other.compare(*this);Bei dir ist das soweit ich sehe nicht erfüllt (Base()==A()).
Mit virtuellen operator== hätte man da exponentiell viele Vergleiche.
-
mael15 schrieb:
dagegen spricht, dass man zum überladen dieselbe signatur benutzen und dann dynamisch casten muss.
Das ist ja im Endeffekt das, was ich auch gleich angemerkt habe, man kann dann leicht einen Vergleichsoperator bauen, der nicht symmetrisch ist. Das Problem ist dabei aber nicht der Operator, sondern der Vergleich.
Dasselbe Problem hätte man doch, wenn man eine Funktion virtual bool equals(const Base& other) hinzufügen würde. Und das ist eine Design Entscheidung, ob man sowas überhaupt haben will und wie man damit umgeht.
Ich meine, wir hätten auch sowas ähnliches bei uns. Wir haben irgendwelche "Container" Klassen, über die man irgendwelche dynamischen Operationen abfragen kann. Und ich glaub, die haben eben auch so eine virtuelle equals Funktion. Die Basisversion ruft irgendwelche allgemeinen Infos ab und vergleicht die. Und es gibt Ableitungen, die mehr vergleichen können. z.B., wenn das Teil mit Informationen aus einem PDM angereichert wurde, auch die PDM ID vergleichen. Das dürfte allerdings nur Sinn machen, wenn man da einen dynamic_cast, sonst wär der Vergleich eben nicht symmetrisch. Ich weiß nicht, ob das bei uns so gemacht wurde oder obs da wo es verwendet wird egal ist. Jedenfalls ist es an der Stelle eben völlig egal, obs eine Operator oder eine Funktion ist und dann kann man genauso einen Operator nehmen.
-
vive schrieb:
mael15 schrieb:
...
Wichtig ist, dass der Vergleichsoperator symmetrisch ist (a==b <=> b==a). Die beste Lösung dazu ist, zwei Vergleiche durchzuführen, wie ich das gezeigt habe:
return compare(other) && other.compare(*this);Das war auch meine 1. Idee. Ist aber potentiell teuer.
Wie wäre es mitreturn typeid(*this) == typeid(other) && compare(other);?
-
hustbaer schrieb:
Wie wäre es mit
return typeid(*this) == typeid(other) && compare(other);?
Kannst du machen, schränkt dich aber u.U. ein (ein SparseVector kann so nicht mehr gleich sein wie ein DenseVector, oder vielleicht verhält sich Klasse A von mael15 gleich wie Base wenn aInt==0).
Können diese Fälle ausgeschlossen werden, ist deine Version natürlich schneller.