Lokale Kopie von Base Klasse anlegen?
-
meinst du sowas ?
#include<iostream> struct Base { virtual int out(int in) = 0; template<typename T> T* create(){ T* e = new T(); return e; } }; struct Derived1 : Base { int out(int in){ return in; } }; struct Derived2 : Base { int out(int in){ return 2*in; } }; template<typename T> void foo(Base &bar, int in) { Base* copy2 = bar.create<T>(); std::cout << copy2->out(in) << '\n'; } int main(void){ Derived1 b1; Derived2 b2; foo<decltype(b1)>(b1, 3); foo<decltype(b2)>(b2, 3); return 0; }3
6
-
großbuchstaben schrieb:
meinst du sowas ?
Nee, dann muss ich ja bei jedem Aufruf von foo immer das template Argument mit angeben. Ich will foo schon ganz normal callen können.
-
happystudent schrieb:
Da diese Methode ja für alle Child-Klassen gleich sein soll, gibt es keine Möglichkeit diese nur einmal (am besten in der Base Klasse) zu definieren?
Die Methode soll ja nicht gleich sein, sie soll nicht mal das gleiche tun.
In Derived1 soll sie tun: eine Derived1-Kopie anlegen.
In Derived2 soll sie tun: eine Derived2-Kopie anlegen.
In Derived3 soll sie tun: eine Derived3-Kopie anlegen.
...Für solche Zwecke eignen sich aber Makros ganz gut.
z.B.:class ThingBase { public: ThingBase* clone() const { return clone_impl(); } private: virtual ThingBase* clone_impl() const = 0; } #define DEFINE_COMMON_THING_MEMBERS(ThingType) \ private: \ virtual ThingType* clone_impl() const \ { \ return new ThingType(*this); \ } \ // end DEFINE_COMMON_THING_MEMBERS class Thing1 : public ThingBase { DEFINE_COMMON_THING_MEMBERS(Thing1); public: void DoThing1Things(); // ... };
-
hustbaer schrieb:
Die Methode soll ja nicht gleich sein, sie soll nicht mal das gleiche tun.
In Derived1 soll sie tun: eine Derived1-Kopie anlegen.
In Derived2 soll sie tun: eine Derived2-Kopie anlegen.
In Derived3 soll sie tun: eine Derived3-Kopie anlegen.
...Ja gut, aber mit dem gleichen Argument könnte ich ja auch sagen cout macht nicht immer das gleiche sondern:
Einmal will ich einen string ausgeben.
Einmal will ich einen double ausgeben.
Einmal will ich einen int ausgeben. etc.Ist ja auch so, trotzdem kann ich das da sehr elegant immer einheitlich machen.
hustbaer schrieb:
Für solche Zwecke eignen sich aber Makros ganz gut.
z.B.:Danke für den Vorschlag, aber ich sehe ehrlich gesagt nicht wie das irgendwas verbessert... Ich muss ja trotzdem wieder das Macro einfügen (was für den User sicher noch wesentlich unverständlicher ist als die abstrakte Methode) und Platz spart es auch nicht wirklich...
Aber dem entnehmen ich dass es keine "saubere" Lösung für so ein Problem gibt, richtig?
Weil eigentlich könnte der Compiler doch bei einer Spracherweiterung wie im folgenden das Leisten oder?
struct Base { virtual int out(int in) = 0; Base::ChildType* create(){ // Oder keine Ahnung wie die Syntax aussehen könnte, könnte man sich bestimmt was hübsches überlegen Base::ChildType* e = new Base::ChildType(); return e; } }; struct Derived1 : Base { int out(int in){ return in; } };Was würde gegen so eine Erweiterung sprechen? Weil ich fände es schon sinnvoll wenn man sowas könnte, würde hier einiges vereinfachen.
-
Wieso geht das nicht so:
template<typename T> T* foo(T& base) { return new T(base); }
-
Naja, ich habe mehrere Derived Klassen die von einer Base Klasse erben. Jetzt will ich in einer Funktion (hier: foo) mit einer Instanz dieser Klassen arbeiten. Allerdings soll der Zustand der Instanz durch diese Funktion nach außen hin nicht verändert werden (intern soll er aber schon upgedatet werden, die Derived Klassen haben ja einen internen Speicher, hier: in_prev).
Letztenlich ist das aber nicht so wichtig, das eigentliche Problem was ich im moment ja habe ist: Ich will eine einheitliche Implementierung einer Funktion in eine Derived Klasse aus einer Base Klasse, die aber den Typ der Derived Klasse verwendet. Das wäre ja auch unabhängig von meinem eigentlichen Problem sinnvoll, da es Code-Duplikate und implementierungs Fehler seitens der User verhindern würde.
EDIT:
Mr.Long schrieb:
Wieso geht das nicht so:
template<typename T> T* foo(T& base) { return new T(base); }Base ist doch abstrakt, davon kann ich ja keine Instanz erstellen oder was meinst du jetzt?
-
Bitte was?
-
happystudent schrieb:
hustbaer schrieb:
Die Methode soll ja nicht gleich sein, sie soll nicht mal das gleiche tun.
In Derived1 soll sie tun: eine Derived1-Kopie anlegen.
In Derived2 soll sie tun: eine Derived2-Kopie anlegen.
In Derived3 soll sie tun: eine Derived3-Kopie anlegen.
...Ja gut, aber mit dem gleichen Argument könnte ich ja auch sagen cout macht nicht immer das gleiche sondern:
Einmal will ich einen string ausgeben.
Einmal will ich einen double ausgeben.
Einmal will ich einen int ausgeben. etc.Ist ja auch so, trotzdem kann ich das da sehr elegant immer einheitlich machen.
Achje bitte keine total unpassenden Vergleiche, hm?
Du willst die *Implementierung* für verschiedene Typen nicht wiederholen, und das nochdazu ohne ein Template zu verwenden.
Das kann iostreams auch nicht. Dort sind die Insertion-Operatoren für int, double, string etc. auch extra ausprogrammiert.
Das einzige was bei iostreams "einheitlich" geht, ist die Syntax bei der *Verwendung*. Und das ist ja bei dir auch kein Problem, auch nicht wenn du die clone() Funktion für 100 verschiedene Klassen 100x per Hand neu runtertippst.
Aufrufen kannst du sie ja immer gleich.happystudent schrieb:
Aber dem entnehmen ich dass es keine "saubere" Lösung für so ein Problem gibt, richtig?
Ich sehe nicht was daran unsauber sein soll.
Aber es gibt noch eine andere Möglichkeit, hatte ich vergessen: CRTP.template <class Derived, class Base> class ThingBase : public Base { Derived* clone() const { return new Derived(*static_cast<Derived const*>(this)); } }; class DerivedThing : public ThingBase<DerivedThing, Thing> { };happystudent schrieb:
Weil eigentlich könnte der Compiler doch bei einer Spracherweiterung wie im folgenden das Leisten oder?
...Was würde gegen so eine Erweiterung sprechen? Weil ich fände es schon sinnvoll wenn man sowas könnte, würde hier einiges vereinfachen.
Sehr sehr viel, aber es würde zu lange dauern das zu erklären.
-
happystudent schrieb:
EDIT:
Mr.Long schrieb:
Wieso geht das nicht so:
template<typename T> T* foo(T& base) { return new T(base); }Base ist doch abstrakt, davon kann ich ja keine Instanz erstellen oder was meinst du jetzt?
In deinem ersten Beispiel, würde der Compiler eine Funktion mit dem impliziet angegebenen Typen compilieren (d.h. du musst keinen < > angeben) welche dir eine Instanz von Derivered1 als Kopie erstellt, so wie du es wolltest, nicht?
Allerdings verstehe ich noch immer nicht was du damit bewirken willst.
-
Mr.Long schrieb:
In deinem ersten Beispiel, würde der Compiler eine Funktion mit dem impliziet angegebenen Typen compilieren (d.h. du musst keinen < > angeben) welche dir eine Instanz von Derivered1 als Kopie erstellt, so wie du es wolltest, nicht?
Allerdings verstehe ich noch immer nicht was du damit bewirken willst.Äh nein, foo erstellt ja keine Kopie, da Derived als Referenz übergeben wird. Was verstehst du denn nicht bei meiner Erklärung?
hustbaer schrieb:
Ich sehe nicht was daran unsauber sein soll.
Naja, ich muss den immer gleichen Code immer wieder in jede Derived Klasse reinkopieren. Komplett redundant und trivial.
hustbaer schrieb:
Sehr sehr viel, aber es würde zu lange dauern das zu erklären.
Kannst du nicht wenigstens ein, zwei kleine Beispiele geben was genau? Wenns so viel ist muss es da ja was geben

Mich interessiert das einfach. Gut, die Syntax war ein bisschen unglücklich gewählt, aber sowas:
struct Base { typedef std::get_child<Base>::type child_type; // So im type traits Stil halt virtual int out(int in) = 0; child_type* create(){ child_type* e = new child_type(); return e; } }; struct Derived1 : Base { // bekommt jetzt ein create implementiert int out(int in){ return in; } };Wenn kein Child vorhanden halt Compilefehler?
-
Wenn du in der Baseklasse unbedingt wissen willst, von was sie gerbt wurdest, kannst du eine virtuelle-Funktion definieren die dann von jeder Kindklasse überschrieben wird und eine eindeutige Object-Id zurück gibt.
-
Ja, aber genauso gut kann ich dann auch gleich die copy methode implementieren lassen. Es geht darum dass ich eine Funktion habe die für alle Child Klassen gleich ist, bis auf den verwendeten Typ.
-
happystudent schrieb:
Naja, ich muss den immer gleichen Code immer wieder in jede Derived Klasse reinkopieren. Komplett redundant und trivial.
Nein, es ist nicht der selbe Code. Er unterscheidet sich im Namen der abgeleiteten Klasse. Wäre es der selbe Code, würde ja auch ein parameterloses Makro reichen, nen?
Ansonsten: Siehe CRTP Lösung oben.
happystudent schrieb:
Mich interessiert das einfach.
Dann denk drüber nach.
happystudent schrieb:
Gut, die Syntax war ein bisschen unglücklich gewählt, aber sowas:
struct Base { typedef std::get_child<Base>::type child_type; // So im type traits Stil halt virtual int out(int in) = 0; child_type* create(){ child_type* e = new child_type(); return e; } }; struct Derived1 : Base { // bekommt jetzt ein create implementiert int out(int in){ return in; } };Wenn kein Child vorhanden halt Compilefehler?
struct Base { typedef std::get_child<Base>::type child_type; virtual int out(int in) = 0; child_type* create(){ child_type* e = new child_type(); return e; } }; struct Derived1 : Base { int out(int in){ return in; } }; struct Derived2 : Base { int out(int in){ return in; } }; Base::child_type foo; // welcher typ? auto bar = &Base::create; // welcher typ? zeiger auf welche Funktion?Uswusf.
Das sind echt so triviale Dinge wo's da gleich hakt, da müsstest du selbst draufkommen.
Und dann macht es nicht viel Spass dir das zu erklären, weil man halt den Eindruck hat dass du da ne sehr verschwommene Idee hattest die du süss findest, und nun meinst du es müsste gehen. Aber ohne dass du dir mal ein paar Minuten Zeit genommen hast die Sache durchzudenken.
-
hustbaer schrieb:
Nein, es ist nicht der selbe Code. Er unterscheidet sich im Namen der abgeleiteten Klasse. Wäre es der selbe Code, würde ja auch ein parameterloses Makro reichen, nen?
Das ist schon klar dass das nicht der selbe Code ist. Aber das ist bei templates ja auch so. Da generiert sich der Compiler ja auch von alleine das zusammen was er braucht und nur so etwas fände ich halt praktisch.
hustbaer schrieb:
Ansonsten: Siehe CRTP Lösung oben.
Ist auch nur (noch) eine andere Schreibweise für das gleiche Problem.
hustbaer schrieb:
Uswusf.
Das sind echt so triviale Dinge wo's da gleich hakt, da müsstest du selbst draufkommen.
Und dann macht es nicht viel Spass dir das zu erklären, weil man halt den Eindruck hat dass du da ne sehr verschwommene Idee hattest die du süss findest, und nun meinst du es müsste gehen. Aber ohne dass du dir mal ein paar Minuten Zeit genommen hast die Sache durchzudenken.Nein, das ist jetzt eine Unterstellung, ich hab mir sehr wohl Zeit genommen. Genauer gesagt denke ich den halben Tag schon darüber nach. Zu deinen Beispielen:
Base::child_type foo; // Compilefehler: child_type nur im Kontext eines childs verfügbar auto bar = &Base::create; // Compilefehler: Base hat kein member create, da dieses nur in childs implementiert wird
-
happystudent schrieb:
hustbaer schrieb:
Nein, es ist nicht der selbe Code. Er unterscheidet sich im Namen der abgeleiteten Klasse. Wäre es der selbe Code, würde ja auch ein parameterloses Makro reichen, nen?
Das ist schon klar dass das nicht der selbe Code ist. Aber das ist bei templates ja auch so. Da generiert sich der Compiler ja auch von alleine das zusammen was er braucht und nur so etwas fände ich halt praktisch.
Du willst doch aber kein Template. Wir bewegen uns im Kreis.
happystudent schrieb:
hustbaer schrieb:
Ansonsten: Siehe CRTP Lösung oben.
Ist auch nur (noch) eine andere Schreibweise für das gleiche Problem.
Also willst du das Feature nur, weil es dir nicht gefällt wenn du den Klassennamen der abgeleiteten Klasse an genau einer Stelle wiederholen musst? WTF?
happystudent schrieb:
hustbaer schrieb:
Uswusf.
Das sind echt so triviale Dinge wo's da gleich hakt, da müsstest du selbst draufkommen.
Und dann macht es nicht viel Spass dir das zu erklären, weil man halt den Eindruck hat dass du da ne sehr verschwommene Idee hattest die du süss findest, und nun meinst du es müsste gehen. Aber ohne dass du dir mal ein paar Minuten Zeit genommen hast die Sache durchzudenken.Nein, das ist jetzt eine Unterstellung, ich hab mir sehr wohl Zeit genommen.
OK, ich hätte "ich" statt "man" schreiben sollen. Und dann ist es gar keine Unterstellung - ICH habe nämlich den Eindruck.
happystudent schrieb:
Base::child_type foo; // Compilefehler: child_type nur im Kontext eines childs verfügbar auto bar = &Base::create; // Compilefehler: Base hat kein member create, da dieses nur in childs implementiert wirdWie willst du create dann über einen Basisklassenzeiger aufrufen, wenn Base kein Member create hat?
-
tl;dr:
Das Problem aus einem Basisklassenzeiger einen Zeiger zu machen, der auf eine entsprechende Derived-Klasse zeigt, ist schon länger bekannt.
Gegen den naheliegenden Ansatz - den mit der virtual clone Funktion - sprechen die Nachteile, die du genannt hast: es ist quasi immer dasselbe, man muss es in jede Basisklasse reinschreiben und kann es bei tieferen Hierachien evtl. vergessen.
Eine andere Methode wäre eine clone-Factory. Die speichert intern eine std::(unordered)_map von dem std::type_index eines Typen und eine entsprechende Funktion, die bei Parameter-Übergabe einen Klon zurückgibt. Man muss sie dann nur bei Programmstart initialisieren.
In etwa so: http://ideone.com/GLH7h4
Das ist dann aber langsamer als die andere Variante.
Die beste Lösung ist es imo, klonen zu vermeiden.
-
happystudent schrieb:
großbuchstaben schrieb:
meinst du sowas ?
Nee, dann muss ich ja bei jedem Aufruf von foo immer das template Argument mit angeben.
mußt du doch gar nicht:
#include<iostream> struct Base { virtual int out(int in) = 0; template<typename T> T* create(){ T* e = new T(); return e; } }; struct Derived1 : Base { int out(int in){ return in; } }; struct Derived2 : Base { int out(int in){ return 2*in; } }; template<typename T> void foo(Base &bar, int in) { Base* copy2 = bar.create<T>(); std::cout << copy2->out(in) << '\n'; } #define Foo(b, i) foo<decltype(b)>(b, i); int main(void){ Derived1 b1; Derived2 b2; Foo(b1, 3); Foo(b2, 3); return 0; }
-
happystudent schrieb:
Lokale Kopie von Base Klasse anlegen?
abgesehen von den mehr oder weniger pfiffigen vorschlägen:
dein vorhaben ist doch unfug.
-
hustbaer schrieb:
Du willst doch aber kein Template. Wir bewegen uns im Kreis.
Das war natürlich nur ein Beispiel:
template <typename T> void foo(T const &t_) { std::cout << t_; } int main() { foo(1); foo("hallo"); foo(2.5); }ist auch jeweils ein anderer Code (dank templates), trotzdem muss ich den template Parameter nicht explizit angeben.
hustbaer schrieb:
Wie willst du create dann über einen Basisklassenzeiger aufrufen, wenn Base kein Member create hat?
Ganz einfach, über ein optional anzugebendes template argument. Wie gesagt, es geht mir eigentlich nur um die Idee, wie das von der Sprache dann letztendlich umgesetzt wird ist ja wieder was anderes. Mit typdef war vielleicht blöd, aber so in der Richtung, mit einem neuen template-typ:
struct Base { template <childname T> T* clone() { return new T(); } // childname bezeichnet den abgeleiteten Typ }; struct Derived : public Base { }; int main() { Derived d; Base *b = d.clone(); // Eindeutig, kann aus dem Kontext hergeleitet werden auto bar = &Base::clone<Derived>; // Nicht eindeutig, Kontext muss explizit angegeben werden }Wäre doch viel besser, oder?
Nathan schrieb:
Die beste Lösung ist es imo, klonen zu vermeiden.
Hm, auch nicht ganz befriedigend aber Danke...
großbuchstaben schrieb:
mußt du doch gar nicht:
Hm, werd ich mal versuchen, kommt schon recht nahe ans Optimum denke ich...
-
Das ist doch alles Quatsch. Ich klinke mich aus.