2 verschiedene Rückgabetypen per Template
-
Genau das suchte ich, Danke.
-
Jetzt gab ich allerding doch noch eine Frage:
class Base { public: virtual int get(void); }; class Dev1 : public Base { public: virtual int get(void); void onlyHere(double n); }; class Dev2 : public Base { public: virtual int get(void); double justThere(void); }; Base* ptr = new Dev1; ptr->get(); //Ok ptr->onlyHere(1); //FailWie kann ich aber auf den Member
onlyHere()zugreifen?
-
heyo schrieb:
Wie kann ich aber auf den Member
onlyHere()zugreifen?Erst einmal gar nicht, weil es ein Designfehler ist, auf die hier gezeigte Art und Weise über einen Basisklassenzeiger auf Member einer abgeleiteten Klasse zuzugreifen. Wenn es unbedingt sein muss (etwa, weil man irgendwelchen alten Code übernommen hat, bei dem eine einmalige Verletzung dieser Regel einem kompletten Neudesign vorzuziehen ist), kann man das über dynamic_cast machen. Aber das ist kein Freibrief, diese Methode bei einem neuen Design bereits einzuplanen. dynamic_cast ist wie const_cast eher ein Zeichen, das im Design etwas nicht stimmt und dient dazu, die Probleme Quick&Dirty zu umgehen.
-
Danke für deine Antwort.
Wie sollte ich das Problem jetzt lösen, sodass ich dennoch auf das gezeigte Beispiel komme? Oder ist das so gar nicht möglich?
-
heyo schrieb:
Danke für deine Antwort.
Wie sollte ich das Problem jetzt lösen, sodass ich dennoch auf das gezeigte Beispiel komme? Oder ist das so gar nicht möglich?Das lässt sich nicht beantworten, weil in der abstrakten Beschreibung mittels "Base", "Derived", usw. nicht zu sagen ist, wo der Fehler in der Modellierung liegt. Es ist eben nur zu sehen, dass es falsch ist, über einen Base-Zeiger einen exklusiven Member von Dev1 ansprechen zu wollen. Denn woher weißt du, ob der Zeiger auf ein Dev1 zeigt?
-
-
SeppJ schrieb:
Das lässt sich nicht beantworten, weil in der abstrakten Beschreibung mittels "Base", "Derived", usw. nicht zu sagen ist, wo der Fehler in der Modellierung liegt. Es ist eben nur zu sehen, dass es falsch ist, über einen Base-Zeiger einen exklusiven Member von Dev1 ansprechen zu wollen. Denn woher weißt du, ob der Zeiger auf ein Dev1 zeigt?
Achso...
boost::variantist soviel ich herausgefunden habe nicht dazu in der lage verschiedene Klassen/Typen zurückzugeben.
-
heyo schrieb:
SeppJ schrieb:
Das lässt sich nicht beantworten, weil in der abstrakten Beschreibung mittels "Base", "Derived", usw. nicht zu sagen ist, wo der Fehler in der Modellierung liegt. Es ist eben nur zu sehen, dass es falsch ist, über einen Base-Zeiger einen exklusiven Member von Dev1 ansprechen zu wollen. Denn woher weißt du, ob der Zeiger auf ein Dev1 zeigt?
Achso...
Du könntest uns genauere Details geben, dann können wir dir bei der Modellierung helfen.
boost::variantist soviel ich herausgefunden habe nicht dazu in der lage verschiedene Klassen/Typen zurückzugeben.Doch. variant<char, int, float> kann entweder einen char, einen int oder einen float halten. variant<c1, c2> also entweder einen c1 oder einen c2.
-
Nathan schrieb:
heyo schrieb:
SeppJ schrieb:
Das lässt sich nicht beantworten, weil in der abstrakten Beschreibung mittels "Base", "Derived", usw. nicht zu sagen ist, wo der Fehler in der Modellierung liegt. Es ist eben nur zu sehen, dass es falsch ist, über einen Base-Zeiger einen exklusiven Member von Dev1 ansprechen zu wollen. Denn woher weißt du, ob der Zeiger auf ein Dev1 zeigt?
Achso...
Du könntest uns genauere Details geben, dann können wir dir bei der Modellierung helfen.
boost::variantist soviel ich herausgefunden habe nicht dazu in der lage verschiedene Klassen/Typen zurückzugeben.Doch. variant<char, int, float> kann entweder einen char, einen int oder einen float halten. variant<c1, c2> also entweder einen c1 oder einen c2.
Welche Details würdet ihr benötigen?
boost::variant wird jedoch nicht so funktionieren wie ich es gerne hätte:
boost::variant<Dev1, Dev2> dat; Dev1 track; Dev2 playlist; dat = track; dat = playlist; template<class T> T getThat(void) { return boost::get<T>(dat); } //Impossible /* T getThat(void) { if(foo) return Dev1; else return Dev2; } */
-
Spricht was gegen
template <typename T> T getThat() { static_assert(false, "Not implemented!"); } template <> int getThat() { return 1; } template <> double getThat() { return 2.5; } int main() { auto varInt = getThat<int>(); // Geht auto varDbl = getThat<double>(); // Geht auch auto varChr = getThat<char>(); // Fehler, not implemented }?
-
heyo schrieb:
Welche Details würdet ihr benötigen?
Was Dev1 und Dev2 repräsentieren.
boost::variant wird jedoch nicht so funktionieren wie ich es gerne hätte:
Für sowas ist apply_visitor glaube ich gedacht.
-
Ja, denn es würde dann nur wieder auf dasselbe hinauslaufen:
if(foo) auto var = getThat<Dev1>; else auto var = getThat<Dev2>; var; //Unsichtbar //-------- auto var; //Nicht möglich if(foo) ...
-
Nathan schrieb:
heyo schrieb:
Welche Details würdet ihr benötigen?
Was Dev1 und Dev2 repräsentieren.
boost::variant wird jedoch nicht so funktionieren wie ich es gerne hätte:
Für sowas ist apply_visitor glaube ich gedacht.
Baserepräsentiert die Basisfunktionen/Variablen, die dannTrack(Dev1) undPlaylist(Dev2) erben.
TrackundPlaylisthaben jedoch noch unabhängige Funktionen, welche nur in den einzelnen Klassen vorhanden sind.
Dann gibt es noch eine Klasse, welche eineTrackund einePlaylistbesitzt, sowie einen indikator, ob es jetzt ein Track oder eine Playlist ist.
Nun wäre es ja geschickt wenn der Endbenutzer der Bibliothek nicht immer beide Anweisungen behandeln muss, obwohl er nur eine benötigt.apply_visitorist jedoch nicht in der Lage (sowie keine andere Funktion) dynamisch andere Typen zurückzugeben.
-
Was ist denn die Gemeinsamkeit von Tracks und Playlists? Dass man sie abspielen kann? Das klingt irgendwie nach Wurstbrot erbt von Supermarkt, weil beide was mit "kaufen" zu tun haben. Klingt eher so, als bestünde eine Playlist aus Tracks.
-
SeppJ schrieb:
Klingt eher so, als bestünde eine Playlist aus Tracks.
Genau das habe ich auch gedacht. In dem Fall bietet sich doch an den Track als Playlist mit nur einem Track (sich selbst) zurück zugeben.
-
Dann würde sich das Composite-pattern anbieten.
-
Ja eine Playlist enthält auch einen
std::vector<Playlist>, jedoch enthält die Klasse Playlist eigene Funktionen, wie zB. die anzahl der Tracks usw. Tracks können jedoch auch alleine sein, heißt also dass Tracks zwar in einer Playlist vorhanden sind, jedoch auch alleine "stehen".
-
heyo schrieb:
Ja eine Playlist enthält auch einen
std::vector<Playlist>, jedoch enthält die Klasse Playlist eigene Funktionen, wie zB. die anzahl der Tracks usw. Tracks können jedoch auch alleine sein, heißt also dass Tracks zwar in einer Playlist vorhanden sind, jedoch auch alleine "stehen".Und wo kommt in dieser Beschreibung eine gemeinsame Basis von Track und Playlist vor?
-
@heyo
D.h. du willst Playlisten aus Tracks und weiteren "Unterplaylisten" zusammensetzen können?
Also wie Verzeichnisse Files und weitere Unterverzeichnisse enthalten können?In dem Fall könnte man z.B. machen...
Variante 1:class PlaylistItem abstrakt class Playlist : PlaylistItem ptr_vector<PlaylistItem> items; class Track : PlaylistItem ...Analogie zu nem File-System wäre:
PlaylistItem entspricht FileSystemEntity
Playlist entspricht Directory
Track entspricht FileVariante 2 (IMO sauberer, da der Track so nix von dem Playlisten-Gedöns wissen muss):
class Playlist abstrakt class SingleTrackPlaylist : Playlist Track track; class CompositePlaylist : Playlist ptr_vector<Playlist> items;Analogie zu nem File-System wäre:
Playlist entspricht FileSystemEntity
SingleTrackPlaylist entspricht File
CompositePlaylist entspricht Directory
Track entspricht dem Inhalt des Files (BLOB)
-
hustbaer schrieb:
class Playlist abstrakt class SingleTrackPlaylist : Playlist Track track; class CompositePlaylist : Playlist ptr_vector<Playlist> items;Und zum Traversieren dann das Visitor-Pattern benutzen. (Kein dynamic_cast!)