polsmorphe klasse mit operator=()
-
Erst mal garnicht.
An dem Punkt in dem ich Schiffe Autos Panzer etc. als Fahrzeuge in eine
liste Packe, ist mir der Typ des Objektes egal. Ich hab alles auf Fahrzeuge
reduziert.Wenn ich die Listen gegeneinander prüfen muss, ob Sie die >>identischen<<
Objekte haben, kann ich dies über das Basisobjekt machen.Wenn es darum geht ob die >>gleichen<< Fahrzeuge in gleicher Reihenfolge
in zwei unterschiedlichen Listen vorhanden sind, sollte man sich Gedanken
machen warum das so ist, denn es passt nicht in die Polymorphy rein.Hierzu fällt mir "nur" ein, jedem Objekt eine virtuelle compare Methode zu
verpassen. Wenn nun aber jemand erbt ohne diese Methode neu zu definieren,
wird später eventuell ein LKW für ein Auto gehalten, weil LKW diese Methode
nicht implementiert hat.Ebenso fehlt mir auf anhieb die Idee, den Typ des zweiten Objektes zu bestimmen.
Alles was mir einfällt wird zu Murks und käme der Idee kaputtes Design sehr nahe.
Lediglich eine virtuelle Methode, die den Typ zurück gibt fänd ich einleuchtend.
Jedoch ergibt sich das Problem, das diese Implementiert sein muss.IMHO kommt man über die typeID und dem Basiszeiger nicht heran:
#include <iostream> #include <string> struct A { virtual std::string name(){return "A";} }; struct B : public A { virtual std::string name(){return "B";} }; struct C : public B { virtual std::string name(){return "C";} }; struct D: public A { virtual std::string name(){return "D";} }; struct E { virtual std::string name(){return "E";} }; struct CE: public E,public C { virtual std::string name(){return "CE";} }; int main(int argc, char* argv[]) { CE ce; C c; CE * ptr_ce = &ce; A * ptr = ptr_ce; B * ptr2 = ptr_ce; C * ptr3 = ptr_ce; E * ptr4 = ptr_ce; B * ptr5 = &c; std::cout<< typeid(ptr_ce).name() << " ist ein " << ptr_ce->name() <<std::endl; std::cout<< typeid(ptr).name() << " ist ein " << ptr->name()<<std::endl; std::cout<< typeid(ptr2).name() << " ist ein " << ptr2->name()<<std::endl; std::cout<< typeid(ptr3).name() << " ist ein " << ptr3->name()<<std::endl; std::cout<< typeid(ptr4).name() << " ist ein " << ptr4->name()<<std::endl; std::cout<< typeid(ptr5).name() << " ist ein " << ptr5->name()<<std::endl; return 0; } // Jup, ich weiß da fehlt nen virt ~ etc.struct CE * ist ein CE
struct A * ist ein CE
struct B * ist ein CE
struct C * ist ein CE
struct E * ist ein CE
struct B * ist ein CMit meinem Wissensstand wird es notwendig jedem Objekt eine Methode zu verpassen,
die Auskunft über den Typ gibt. Je nachdem was mit dem Vergleich der Listen erreicht
werden muss, lässt sich der Vergleich aber ganz umgehen.So allgemein gehalten fühlt sich die Notwendigkeit einen solchen vergleich machen
zu müssen falsch und riskant an, sobald eine klasse die erbt die implementierung hierfür
nicht bereit stellt , wird es ein falsches Ergebnis liefern. Was soll passieren, wenn
Mehrfachvererbung ins Spiel kommt ? Was wenn ich ein schwimmendes Auto haben will und so
vererbe ?struct Fahrzeug { virtual void fahren() = 0; }; struct Auto : public Fahrzeug { virtual void fahren(){ std::cout<<"brumm"<<std::endl;} }; struct Schiff : public Fahrzeug { virtual void fahren(){ std::cout<<"blubber"<<std::endl;} }; struct schwimmendesAuto : public Auto, public Schiff { virtual void fahren(){ Schiff::fahren(); Auto::fahren();} }; Fahrzeug * f = reinterpret_cast<Fahrzeug*>(new schwimmendesAuto); f->fahren();Eventuell fehlt mir aber auch KnowHow oder ich habe den Beitrag nicht genau genug gelesen.
-
Knuddlbaer schrieb:
Wenn es darum geht ob die >>gleichen<< Fahrzeuge in gleicher Reihenfolge
in zwei unterschiedlichen Listen vorhanden sind, sollte man sich Gedanken
machen warum das so ist, denn es passt nicht in die Polymorphy rein.Ja, es geht um Gleicheit, nicht um Identität.
Und warum passt ein Vergleich zweier Listen nicht in die Polymorphie?
Wie soll man eine Reihe von dynamischen Objekten, die in einer Liste verwaltete werden, sonst vergleichen?!
Ob die Objekte polymorph sind, oder nicht ist doch erstmal egal. Die Polymorphie sollte mir allenfalls *helfen*, ohne ständige Typunterscheidungen mit den Objekten in der Liste zu arbeiten. Sie sollte mir helfen dieses Ziel zu erreichen, *behindern* kann sie es doch kaum!?
Knuddlbaer schrieb:
Hierzu fällt mir "nur" ein, jedem Objekt eine virtuelle compare Methode zu
verpassen. Wenn nun aber jemand erbt ohne diese Methode neu zu definieren,
wird später eventuell ein LKW für ein Auto gehalten, weil LKW diese Methode
nicht implementiert hat.Und genau das ist der Punkt. Das wäre ja genau die Lösung.
Polymorphie entsteht durch virtuelle Funktionen. Ich will also eine virtuelle Compare-methode schreiben (oder eben einen virtuellen operator==()). Die Frage ist aber: Wie? Das problem ist, daß die virtuellen Methoden, die mir einfallen würden, immer ein dynamic_cast benötigen.
Alle sagen mir ich mache einen Design-Fehler. Aber keiner kann mir wirklich erklären *welchen*.
Das beispiel mit den Fahrzeugen in zwei Listen ist doch logisch nachvollziehbar. Solange mir keiner sagt, warum der Wunsch ilegitim sein, zwei solcher Listen zu vergleichen, suche ich nach einer implementation.
Aber dafür hat auch niemand eine Lösung. Soll ich nicht vererben? Soll ich keine Liste mit Basiszeigern verwenden? Soll ich keine Polymorphie verwenden? oder soll ich schlicht dynamic_cast benutzen, ohne schlechtes Gewissen?
-
Na, nen Krümel hin werfen und verlangen das jemand den ganzen Vorgang fürs Brotbacken erklärt funktioniert nicht.
Erkläre doch mal >>Warum<< Du diesen Abgleich der Listen benötigst.
Solange Du nicht sagen kannst warum Du den abgleich der Listen brauchst, ist er unnötig. Die Antwort ist wenig Hilfreich aber genauso schön allgemein.AFAIK gibt es keine Möglichkeit aus einem Basiszeiger die Information herauszubekommen was er denn mal war.
Ich habe bisher nie einen tiefen vergleich benötigt. Ich habe stehts einen Weg über eine virtuelle Methode gewählt.
Wie würde der vergleich denn mit dynamic_cast aussehen ? Vllt. hilft mir das den Horizont zu erweitern und ne Idee zu liefern.
-
Wenn du es wirklich willst, und wie gesagt, ich halte es fuer eher kaputt, kannst du natuerlich die standard variante mit dynamic_cast machen (was anderes geht nicht):
virtual void compare(Base& o) { Derived& other=dynamic_cast<Derived&>(other); return this.foo == other.foo; }aber an Bloch denken, der in effective Java schreibt:
There is simply no way to
extend an instantiable class and add an aspect while preserving the equals
contract.und equals bezieht sich hier auf unsere compare methode.
solche vergleiche mit nicht-value klassen sind oft ein problem. es macht idR auch keinen sinn solche vergleiche zu verlangen...
-
Ok, auch wenn ich nix zu beitragen konnte, hat mir der Beitrag was gebracht

thx @ Shade
-
Knuddlbaer schrieb:
Da alle Basisobjekte gleich sind, ist mir nie das Bedürfnis gekommen diese auch zu vergleichen.
In einem Objekt etwas anderes zu sehen als das Basisobjekt unterläuft irgendwie den Sinn der Sache.
Basisobjekt == Basisobjekt, alles andere lässt man virtuelle Methoden erledigen.

Die Argumentation kann ich nicht nachvollziehen. Wieso sind alle Basisobjekte gleich? Sind dann auch alle ints gleich? int==int und ints vergleichen macht keinen Sinn?
-
Weil ein Basisobjekt ein Basisobjekt ist ?
Also ein Fahrzeug ein Fahrzeug und kein Schiff Panzer oder sonst was ?!
Ich kann ein Basisobjekt mit einem Basisobjekt vergleichen wie ein int mit einem int. Ich kann aber kein std::string mit einem int vergleichen wie ich kein schiff mit einem panzer vergleichen kann.
-
Vielleicht würden sich alle leichter tun wenn du statt Autos und Booten mal konkret schreibst um was es geht (oder hab ich das überlesen?).
Ansonsten sehe ich das Problem nicht. Die Lösung heisst typeid. Guckst du:#include <cassert> #include <string> #include <memory> #include <iostream> class Vehicle { public: Vehicle(int maxSpeed) : m_maxSpeed(maxSpeed) { } bool operator == (Vehicle const& other) const // final { // virtual call to CompareImpl of whatever class (*this) is return CompareImpl(other); } bool operator != (Vehicle const& other) const // final { // virtual call to CompareImpl of whatever class (*this) is return !CompareImpl(other); } // virtual interface: virtual std::string Foo() { return "Vehicle"; } protected: // compare the Vehicle part of 2 Vehicles bool CompareVehicles(Vehicle const& other) const { return m_maxSpeed == other.m_maxSpeed; } private: // only ever called by operators == and != of Vehicle // override in every derived class virtual bool CompareImpl(Vehicle const& other) const { assert(typeid(*this) == typeid(Vehicle)); // (*this) MUST be a Vehicle (and NO derived class) if(typeid(other) == typeid(Vehicle)) return CompareVehicles(other); else return false; } int m_maxSpeed; }; class Car : public Vehicle { public: Car(int maxSpeed, int wheelCount) : Vehicle(maxSpeed), m_wheelCount(wheelCount) { } // virtual interface: virtual std::string Foo() { return "Car"; } protected: // compare the Car part of 2 Cars bool CompareCars(Car const& other) const { return CompareVehicles(other) && (m_wheelCount == other.m_wheelCount); } private: // see Vehicle::CompareImpl virtual bool CompareImpl(Vehicle const& other) const { assert(typeid(*this) == typeid(Car)); // (*this) MUST be a Car (and NO derived class) if(typeid(other) == typeid(Car)) // since we now know the exact type of (other) we don't need a slow dynamic_cast return CompareCars(static_cast<Car const&>(other)); else return false; } int m_wheelCount; }; class Boat : public Vehicle { public: Boat(int maxSpeed, int draught) : Vehicle(maxSpeed), m_draught(draught) { } // virtual interface: virtual std::string Foo() { return "Boat"; } protected: bool compareBoats(Boat const& other) const { return CompareVehicles(other) && (m_draught == other.m_draught); } private: // see Vehicle::CompareImpl virtual bool CompareImpl(Vehicle const& other) const { assert(typeid(*this) == typeid(Boat)); // (*this) MUST be a Boat (and NO derived class) if(typeid(other) == typeid(Boat)) // since we now know the exact type of (other) we don't need a slow dynamic_cast return compareBoats(static_cast<Boat const&>(other)); else return false; } int m_draught; }; int main(int argc, char** argv) { std::auto_ptr<Vehicle> vehicle(new Vehicle(10)); std::auto_ptr<Vehicle> car1(new Car(10, 4)); std::auto_ptr<Vehicle> car2(new Car(10, 4)); std::auto_ptr<Vehicle> car3(new Car(10, 6)); std::auto_ptr<Vehicle> boat1(new Boat(10, 4)); std::auto_ptr<Vehicle> boat2(new Boat(10, 4)); std::auto_ptr<Vehicle> boat3(new Boat(10, 99)); assert(*vehicle == *vehicle); assert(*car1 != *vehicle); assert(*boat1 != *vehicle); assert(*car1 == *car2); assert(*car1 != *car3); assert(*car2 != *car3); assert(*boat1 == *boat2); assert(*boat1 != *boat3); assert(*boat2 != *boat3); assert(*car1 != *boat1); assert(*car2 != *boat1); //... std::cout << "Vehicle: " << vehicle->Foo() << std::endl; std::cout << "Car: " << car1->Foo() << std::endl; std::cout << "Boat: " << boat1->Foo() << std::endl; std::cout << std::flush; return 0; }Funktioniert wunderbar. Und typeid(a) == typeid(b) würde ich immer einem dynamic_cast vorziehen, ganz einfach weil es schneller ist, und weil du mit dynamic_cast einen doofen Tanz aufführen musst um draufzukommen ob denn das zu testende Objekt wirklich ein "Bar" ist oder nur ein von "Bar" abgeleitetes etwas. Gehen tut es schon, sieht dann aber so aus (*würg*):
Vehicle* v = GimmeSomeVehicle(); // see if v is a car (and NOT an instance of a derived type) if((dynamic_cast<Car*>(v) != 0) && (dynamic_cast<void*>(v) == static_cast<Car*>(v))) DoSomethingWithCar(static_cast<Car*>(v));Erklärung: dynamic_cast<void*> gibt die Adresse des "most derived type" zurück. Wenn diese gleich der Adresse von "v auf Car gecastet" ist, dann muss v ein Car sein und kein von Car abgeleiteter Typ. Da wir aber nicht einfach v auf Car* casten können ohne überhaupt zu wissen ob v ein Car oder von Car abgeleiteter Typ ist müssen wir das vorher nochmal prüfen, daher der Test (dynamic_cast<Car*>(v) != 0).
Wie gesagt alles viel zu kompliziert, von daher lieber typeid verwenden.
Falls die Objekte um die es geht serialisierung unterstützen ist es überhaupt recht einfach, denn dann musst du bloss die serialisierten Daten vergleichen. In den serialisierten Daten muss ja wohl eine Art eindeutiger Typenkennung vorkommen, sonst kannst du sie ja nie mehr deserialisieren.
Ahja, das Zuweisen... . Entweder du klonst wirklich die Objekte anstatt Zuweisungn zu verwenden (dann musst du eben damit leben das Alte wegzuwerfen und ein Neues zu machen), oder aber du wirst auf das Problem stossen dass du die Zuweisung "DerivedType = BaseType;" irgendwie behandeln musst. Entweder man verbietet es und du wirfst einen std::logic_error, oder aber du musst den DerivedType mit irgendwelchen Defaultwerten füttern. Auch doof. Lässt sich aber auf jeden Fall gleich wie die Operatoren == und != oben umsetzen.
Im übrigen gehe ich auch davon aus dass es ein eleganteres Design geben müsste. Kann ich aber nicht sagen solange du nicht sagst worum es konkret geht.
-
Hi,
nur mal als andere Technik reingeworfen (habe keine Ahnung, ob das für Dich passt): operator==() kann man auch als freie binäre Funktion definieren. Es muß keine Memberfunktion sein.
Damit könntest Du entsprechende Overloads für jedes Typpaar bereitstellen (vielleicht soll ja ein Vergleich "LKW == Schiff" anders vonstatten gehen als "LKW == Auto" ...)
Kann natürlich recht aufwendig sein, wenn man viele Typen hat ... und man sollte die "symmetrische Version" nicht vergessen, damit "LKW == Schiff" nichts anderes ergibt als "Schiff == LKW" ... es sei denn man will genau das.
Und man muß sich natürlich "Generiziztät" ein wenig anders nähern.Nur mal so als Idee....
(gilt natürlich auch für operator=())Gruß,
Simon2.
-
Simon2 schrieb:
nur mal als andere Technik reingeworfen (habe keine Ahnung, ob das für Dich passt): operator==() kann man auch als freie binäre Funktion definieren. Es muß keine Memberfunktion sein.
Damit könntest Du entsprechende Overloads für jedes Typpaar bereitstellen (vielleicht soll ja ein Vergleich "LKW == Schiff" anders vonstatten gehen als "LKW == Auto" ...)Ne, geht nicht. C++ kann keine multimethods.
Du hantierst ja nur mit Base* rum... und dann nimmt C++ auch nur den Typ Base* zum aufloesen welche Funktion aufgerufen wird.
-
Shade Of Mine schrieb:
...Du hantierst ja nur mit Base* rum...
Stimmt schon, ist ja Laufzeitpolymorphie - hatte ich im ersten Elan gar nicht gesehen. *hand-an-die-stirn-klatsch*
Dabei hilft natürlich die (template-)STL und das Overloading mit ihrer Compilezeitpolymorphie nicht wirklich weiter ....Tja, wenn's Laufzeit sein muß, kann ich auch nicht weiterhelfen.
Schönen Abend noch,
Simon2.
-
Shade Of Mine schrieb:
Wenn du es wirklich willst, und wie gesagt, ich halte es fuer eher kaputt, kannst du natuerlich die standard variante mit dynamic_cast machen (was anderes geht nicht):
virtual void compare(Base& o) { Derived& other=dynamic_cast<Derived&>(other); return this.foo == other.foo; }Hehe.. dachte schon dass sich da ein kleiner std::bad_cast eingeschlichen hat. Aber es geht ja gar nicht um equals geht.
Meine Variante:
virtual int compare( base const& other ) const { throw std::runtime_exception("This method shouldn't exist, let alont be called!"); } virtual bool equals( base const& other ) const { // if you must, why the heck not? if (derived const* d = dynamic_cast<derived const*>(&other)) { return some_attribute() == other->some_attribute(); } else { return false; } }
-
finix schrieb:
Hehe.. dachte schon dass sich da ein kleiner std::bad_cast eingeschlichen hat.
der ist absicht.
damits "bumm" geht, wenn jemand mist baut.
aber das ist halt je nachdem wie man es lieber hat...
-
Ist mir dann auch klar geworden. Dachte ja auch erst es geht um equals.
-
Knuddlbaer schrieb:
Weil ein Basisobjekt ein Basisobjekt ist ?
Also ein Fahrzeug ein Fahrzeug und kein Schiff Panzer oder sonst was ?!
Ich kann ein Basisobjekt mit einem Basisobjekt vergleichen wie ein int mit einem int. Ich kann aber kein std::string mit einem int vergleichen wie ich kein schiff mit einem panzer vergleichen kann.
So? Ich kann ein Schiff mit einem Panzer vergleichen. Und solange ich zu dem Ergebnis komme, daß sie ungleich sind, will ich mal denjenigen sehen, der mir da widerspricht

-
Plotzenhotz schrieb:
Vielleicht würden sich alle leichter tun wenn du statt Autos und Booten mal konkret schreibst um was es geht (oder hab ich das überlesen?).
Nein, hast du nicht überlesen. Da müsste ich Euch die halbe Anwendung erklären. Glaubt mir einfach daß Boote und Fahrzeuge es genau so tun.
Plotzenhotz schrieb:
Ansonsten sehe ich das Problem nicht. Die Lösung heisst typeid. Guckst du:
(...)
Sehr gute Lösung, das habe ich gesucht. Meine bisherige Lösung ähnelt deiner ein wenig, aber deine hat einen großen Vorteile: Bei mir wäre für jede Vererbungsebene einmal eine RTTI-Operation gemacht worden (ich habe keine weitere, interne Vegleichsmethode benutzt). Darum wäre bei mir der dynamic_cast zwar einfacher gewesen, als in deinem Beispiel, aber eben verschachtelt. Deine Lösung ist viel besser.
Nebenbei gesagt, kannte ich die variante dynamic_cast<void*>() noch nicht. Asche auf mein Haupt.
Was das typeid anbelangt, frage ich mich, warum hast du den Vergleich nicht generell in operator ==() gemacht? Also so:
bool Vehicle::operator == (Vehicle const& other) const { if(typeid(other) == typeid(*this)) return CompareImpl(other); else return false; } // ....... virtual bool Car::CompareImpl(Vehicle const& other) const { assert(typeid(*this) == typeid(Car)); assert(typeid(other) == typeid(Car)); return CompareCars(static_cast<Car const&>(other)); }Das spart 'ne Menge Code, nicht? Habe ich was übersehen?
Plotzenhotz schrieb:
Funktioniert wunderbar. Und typeid(a) == typeid(b) würde ich immer einem dynamic_cast vorziehen, ganz einfach weil es schneller ist.
Es ist schneller? Hast du den Nerv mir das zu erklären?
Plotzenhotz schrieb:
Falls die Objekte um die es geht serialisierung unterstützen ist es überhaupt recht einfach, denn dann musst du bloss die serialisierten Daten vergleichen. In den serialisierten Daten muss ja wohl eine Art eindeutiger Typenkennung vorkommen, sonst kannst du sie ja nie mehr deserialisieren.
Daran hatte ich gedacht, aber die Objekte, in meinem Fall, sind (z.Zt.) nicht serialisierbar. Auch wäre diese Lösung wohl ziemlich langsam.
Plotzenhotz schrieb:
Ahja, das Zuweisen... . Entweder du klonst wirklich die Objekte anstatt Zuweisungn zu verwenden (dann musst du eben damit leben das Alte wegzuwerfen und ein Neues zu machen), oder aber du wirst auf das Problem stossen dass du die Zuweisung "DerivedType = BaseType;" irgendwie behandeln musst. Entweder man verbietet es und du wirfst einen std::logic_error, oder aber du musst den DerivedType mit irgendwelchen Defaultwerten füttern. Auch doof. Lässt sich aber auf jeden Fall gleich wie die Operatoren == und != oben umsetzen.
Defaultwerte ist in der Tat dämlich. Da ich im Augenblick keine wirkliche Notwendigkeit habe, Elemente über Basiszeiger polymorph *zuzuweisen*, habe ich mich entschieden zu Clonen. Damit erledigen sich solche Zicken.
Ich habe für meinen polymorphen Container nur einen operator=() geschrieben der alle Elemente des Containers löscht und Clone der Elemente des zugewiesenen Containers einhängt.
Ich habe keine Lust, ohne echte Not "DerivedType = BaseType;" zu behandeln. Im Gegensatz zu "Auto == Schiff" muss ich nämlich den Vorredner recht geben, daß sich "Auto = Schiff" fürchterlich beißt.Du hast mir wirklich Klarheit geschaffen. Nicht nur deine == Lösung ist besser, ich bin jetzt auch sicher, daß es keine Lösung ohne RTTI gibt und daß ich nichts elementares übersehen habe. Vielen Dank!
-
Ja, ich war gedanklich viel zu wenig bei der RTTI geschichte.
Ich stell mich ja schon ins Eck

-
Jordy schrieb:
...
Plotzenhotz schrieb:
Funktioniert wunderbar. Und typeid(a) == typeid(b) würde ich immer einem dynamic_cast vorziehen, ganz einfach weil es schneller ist.
Es ist schneller? Hast du den Nerv mir das zu erklären?
...Ich bin zwar eigentlich nicht angesprochen, aber der Angesprochene schrieb hier:
Plotzenhotz schrieb:
...Ich weiss bloss dass MSVC strcmp() aufruft - und je tiefer er suchen muss, umso öfter. Und strcmp() ist halt langsamer als Hashcodes oder gleich UIDs zu vergleichen.
Ob das andere Compiler anders machen weiss ich nicht, kann leicht sein dass die meisten Compiler hersteller dynamic_cast nicht als wichtig genug sehen um das ordentlich zu optimieren.
Der Vergleich von type_info Instanzen ist übrigens auch über strcmp() implementiert, aber da wird wenigstens immer bloss ein String verglichen.
Gruß,
Simon2.
-
Jordy schrieb:
Plotzenhotz schrieb:
Vielleicht würden sich alle leichter tun wenn du statt Autos und Booten mal konkret schreibst um was es geht (oder hab ich das überlesen?).
Nein, hast du nicht überlesen. Da müsste ich Euch die halbe Anwendung erklären. Glaubt mir einfach daß Boote und Fahrzeuge es genau so tun.
* Was ist das für eine Liste?
* Für welche Operation(en) brauchst du den Test auf Gleichheit?
* etc
-
Was das typeid anbelangt, frage ich mich, warum hast du den Vergleich nicht generell in operator ==() gemacht? Also so: (...)
Weil ich nicht daran gedacht habe bzw. es mir nicht aufgefallen ist

Das verwendete Pattern ist halt allgemein, und mir ist nicht aufgefallen dass ich ja nen Teil der Funktionalität in dem Fall in den operator == raufziehen kann. Normalerweise ist die public Funktion die die virtuelle private "Impl" Funktion aufruft ein dummer Stub der selber nix tut.Nochwas zur Frage warum type_info mit == zu vergleichen schneller ist als ein dynamic_cast: ich weiss nicht 100% ob das mit allen Compilern der Fall ist, aber ich denke bei type_info == vs. dynamic_cast *sollte* type_info == immer besser abschneiden. Einfach deswegen weil nur 2 konkrete Typen verglichen werden müssen, und nicht ein Typ in einer (möglicherweise recht langen) Liste von erreichbaren (und vom Ausgangspunkt aus eindeutig erreichbaren - vonwegen "multiple inheritance" ohne "virtual inheritance" und so - "the dreaded diamond") Basistypen gesucht werden muss. Selbst wenn man die Suche in der Liste irgendwie optimiert wird man es wohl kaum auf einen einzigen Vergleich/Lookup reduzieren können.
Was ich aber nicht ganz verstehe ist warum MSVC nicht wenigstens pre-hashed Strings hernimmt, wenn er schon unbedingt nen String Vergleich machen muss. Das ginge dann doch etwas flotter. Zwar nur eine lineare Optimierung, dafür sicher > Faktor 2 (wahrscheinlich eher Faktor 10 oder mehr für lange Namen die sich erst weit hinten unterscheiden).
Daran hatte ich gedacht, aber die Objekte, in meinem Fall, sind (z.Zt.) nicht serialisierbar. Auch wäre diese Lösung wohl ziemlich langsam.
Naja, wie langsam das ist kommt darauf an wie man serialisiert und wohin. Im Normalfall wirst du aber zumindest eine dynamische Speicheranforderung haben, und das kostet auch nicht gerade wenig. Allerdings wahrscheinlich auch nicht um Welten langsamer als die type_info Lösung.
ich bin jetzt auch sicher, daß es keine Lösung ohne RTTI gibt
Jain. Es gibt keine Lösung wo du nicht die Klassen irgendwie eindeutig identifizieren musst, was aber nicht unbedingt über RTTI sein muss. Eine einfache "virtual std::string GetClassName() const" Funktion würde schon ausreichen, bloss is das wieder viel Aufwand das überall nachzuziehen, und auch z.T. gefählich, denn wenn jmd. vergisst z.B. SportsCar::GetClassName zu deklarieren wird Car::GetClassName hergenommen, dann gibts ganz hässliche Probleme die u.U. sehr blöd zu debuggen sind. Siehe auch MFC, die tun auch fest rumcasten und vergleichen ohne RTTI, halt über das selbstgestrickte CRuntimeClass System.