Vorwärtsdeklaration
-
DStefan schrieb:
Nun ja... warum es nicht geht, sagt schon die Fehlermeldung: Terrain ist in Quadtree.h unbekannt.
Das versteh ich nicht. Natürlich ist Terrain nicht bekannt, deshalb benutz ich ja die Forward Declaration. Das ist doch der Sinn einer Forward Declaration? Ich sage dem Compiler: Jetzt kommt gleich ein Typ, für den kennst du noch keine Deklaration, aber das passt schon.
Ist meine Syntax falsch? Oder ist das Problem, dass SharedData eine innere Klasse ist?
-
München schrieb:
DStefan schrieb:
Nun ja... warum es nicht geht, sagt schon die Fehlermeldung: Terrain ist in Quadtree.h unbekannt.
Das versteh ich nicht. Natürlich ist Terrain nicht bekannt, deshalb benutz ich ja die Forward Declaration. Das ist doch der Sinn einer Forward Declaration? Ich sage dem Compiler: Jetzt kommt gleich ein Typ, für den kennst du noch keine Deklaration, aber das passt schon.
Ist meine Syntax falsch? Oder ist das Problem, dass SharedData eine innere Klasse ist?Letzteres. Terrain muss bekannt sein, damit du ein Element von Terrain forward deklarieren kannst. Einen Namespace würdest du hierzu einfach ein weiteres Mal öffnen, im Falle einer Klasse kannst du das Gewünschte gar nicht sagen.
Stefan.
-
Das heißt im Klartext, dass eine Vorwärtsdeklartion auf eine Innere Klasse immer UNMÖGLICH ist?
-
München schrieb:
Das heißt im Klartext, dass eine Vorwärtsdeklartion auf eine Innere Klasse immer UNMÖGLICH ist?
Ja, siehe http://www.c-plusplus.info/forum/viewtopic-var-t-is-234000.html
-
Nexus schrieb:
München schrieb:
Das heißt im Klartext, dass eine Vorwärtsdeklartion auf eine Innere Klasse immer UNMÖGLICH ist?
Ja, siehe http://www.c-plusplus.info/forum/viewtopic-var-t-is-234000.html
... und das ist doch auch konsequent! Du darfst etwas nicht verwenden, das nicht definiert ist. Und eine solche forward-Deklaration verwendet die äußere Klasse. Ich würde es schon ziemlich "unnatürlich" finden, wenn eine solche Deklaration akzeptiert würde.
Stefan.
-
DStefan schrieb:
... und das ist doch auch konsequent! Du darfst etwas nicht verwenden, das nicht definiert ist. Und eine solche forward-Deklaration verwendet die äußere Klasse. Ich würde es schon ziemlich "unnatürlich" finden, wenn eine solche Deklaration akzeptiert würde.
Ja, das ist der aktuelle Grund, wieso es nicht funktioniert, und macht so gesehen auch Sinn. Aber ganz konsequent finde ich es trotzdem nicht. Eine Vorwärtsdeklaration soll sagen, dass es einen Typen mit diesem Namen gibt - Nested hin oder her. Dass man die Klasse zusätzlich angibt, dient ja nur der Typsicherheit, deswegen muss die Klasse nicht zwingend bekannt sein (ok, im Status Quo schon, aber es wäre sicher kein Problem, für Vorwärtsdeklarationen von Nested Types diesbezüglich eine Ausnahme zu machen).
So, wie es momentan gelöst ist, wird man gezwungen, die innere Klasse aus der äusseren zu nehmen oder mit
void*zu arbeiten und die Typsicherheit aufzugeben. Beides finde ich nicht unbedingt das Wahre.Ähnliches sieht man ja zum Beispiel auch bei
enums, die momentan nicht vorwärtsdeklariert werden können. Hier soll sich der neue Standard ja auch ändern.
-
Nexus schrieb:
DStefan schrieb:
... und das ist doch auch konsequent! Du darfst etwas nicht verwenden, das nicht definiert ist. Und eine solche forward-Deklaration verwendet die äußere Klasse. Ich würde es schon ziemlich "unnatürlich" finden, wenn eine solche Deklaration akzeptiert würde.
Ja, das ist der aktuelle Grund, wieso es nicht funktioniert, und macht so gesehen auch Sinn. Aber ganz konsequent finde ich es trotzdem nicht. Eine Vorwärtsdeklaration soll sagen, dass es einen Typen mit diesem Namen gibt - Nested hin oder her. Dass man die Klasse zusätzlich angibt, dient ja nur der Typsicherheit, deswegen muss die Klasse nicht zwingend bekannt sein (ok, im Status Quo schon, aber es wäre sicher kein Problem, für Vorwärtsdeklarationen von Nested Types diesbezüglich eine Ausnahme zu machen).
So, wie es momentan gelöst ist, wird man gezwungen, die innere Klasse aus der äusseren zu nehmen oder mit
void*zu arbeiten und die Typsicherheit aufzugeben. Beides finde ich nicht unbedingt das Wahre.Ähnliches sieht man ja zum Beispiel auch bei
enums, die momentan nicht vorwärtsdeklariert werden können. Hier soll sich der neue Standard ja auch ändern.Ich verstehe deine Argumentation. Und es ist ja nicht so, dass ich die Sprache "besudelt" fände, sollte eine solche Deklaration möglich sein. (Falls du verstehst, was ich meine
) Technisch ist eine solche Deklaration ganz Ok.Meine Bedenken liegen eher auf der Bedeutungs-Ebene. Was bedeutet es (bei einem wohl ausgewogenen Design), wenn eine Klasse in einer anderen enthalten ist? Die innere Klasse ist ein Teil der Definition der äußeren, sie gehört ihr. Sie bedeutet nur etwas im Kontext der äußeren Klasse. Deshalb spreche ich von Konsequenz, wenn eine solche forward-Deklaration nicht möglich ist. Es würde mich einfach irritieren, solchen Code zu lesen. Etwas in der Art....
Was die forward-Deklaration von enums betrifft, sehe ich den Sinn nicht. Wofür würde man das benötigen? Hast du ein Beispiel?
Stefan.
-
DStefan schrieb:
Meine Bedenken liegen eher auf der Bedeutungs-Ebene. Was bedeutet es (bei einem wohl ausgewogenen Design), wenn eine Klasse in einer anderen enthalten ist? Die innere Klasse ist ein Teil der Definition der äußeren, sie gehört ihr. Sie bedeutet nur etwas im Kontext der äußeren Klasse. Deshalb spreche ich von Konsequenz, wenn eine solche forward-Deklaration nicht möglich ist. Es würde mich einfach irritieren, solchen Code zu lesen. Etwas in der Art....
Stimmt, da hast du völlig Recht. Meistens würde es auch eine freie Hilfsklasse tun.
Ich hatte bei mir ein
public-Nested-Struct, das bestimmte Eigenschaften der äusseren Klasse zusammenfasste und als Ganzes herumgegeben werden konnte. Die einzige Instanz dieser inneren Klasse liegt in der äusseren. Mir schien hierbei die logische Bindung recht stark, sodass ich die Klasse innerhalb deklarierte. Aber Klassen, die impublic-Bereich deklariert werden, sind sowieso ein bisschen speziell. Das ist schon einige Zeit her, heute würde ich wohl eher beide Klassen frei implementieren und gegebenenfalls in der einen (ursprünglich äusseren) eintypedefauf die andere einrichten. Mich hat es anfänglich halt ein wenig erstaunt, dass so eine Vorwärtsdeklaration nicht funktioniert. Aber inzwischen halte ich das nicht mehr für so schlimm...
DStefan schrieb:
Was die forward-Deklaration von enums betrifft, sehe ich den Sinn nicht. Wofür würde man das benötigen? Hast du ein Beispiel?
Es hat eigentlich den gleichen Sinn wie bei Klassen: Man möchte nicht den Header mit der Definition einbinden, weil man nicht auf den Typ zugreift, sondern nur weiss, dass es sich um einen bestimmten Typen handelt - letztendlich also zur Gewährung von Typsicherheit.
Hm, anstatt hier ein künstliches Beispiel zu konstruieren, kann ich dir gleich einen Anwendungsfall von mir zeigen (es geht um ein Spiel):
// ==== Item.hpp ================================== enum ItemType { // einige Gegenstandstypen }; // Item-Klasse und weiteres...// ==== Player.hpp ================================ #include "Item.hpp" // <- momentan erforderlich class Player { public: void CollectItem(ItemType NewItem); };Playerbesitzt hier kein Member des TypsItemType, sondern führt beim Einsammeln je nach Gegenstand unterschiedliche Aktionen aus, die inCollectItem()geregelt sind.ItemTypewird also nur für diese eine Funktion gebraucht. Hier würde eine Vorwärtsdeklaration reichen, dann müsste man den Header"Item.hpp"nicht einbinden (ansonsten braucht der Spieler nämlich nichts über Gegenstände zu wissen). Erst in der"Player.cpp"wäre"Item.hpp"dann inkludiert.
-
Nexus schrieb:
DStefan schrieb:
Was die forward-Deklaration von enums betrifft, sehe ich den Sinn nicht. Wofür würde man das benötigen? Hast du ein Beispiel?
Es hat eigentlich den gleichen Sinn wie bei Klassen: Man möchte nicht den Header mit der Definition einbinden, weil man nicht auf den Typ zugreift, sondern nur weiss, dass es sich um einen bestimmten Typen handelt - letztendlich also zur Gewährung von Typsicherheit.
Hm, anstatt hier ein künstliches Beispiel zu konstruieren, kann ich dir gleich einen Anwendungsfall von mir zeigen (es geht um ein Spiel):
// ==== Item.hpp ================================== enum ItemType { // einige Gegenstandstypen }; // Item-Klasse und weiteres...// ==== Player.hpp ================================ #include "Item.hpp" // <- momentan erforderlich class Player { public: void CollectItem(ItemType NewItem); };Playerbesitzt hier kein Member des TypsItemType, sondern führt beim Einsammeln je nach Gegenstand unterschiedliche Aktionen aus, die inCollectItem()geregelt sind.ItemTypewird also nur für diese eine Funktion gebraucht. Hier würde eine Vorwärtsdeklaration reichen, dann müsste man den Header"Item.hpp"nicht einbinden (ansonsten braucht der Spieler nämlich nichts über Gegenstände zu wissen). Erst in der"Player.cpp"wäre"Item.hpp"dann inkludiert.Hmmm - ich verstehe. Das wäre dann der zweite Anwendungsfall von forward-Deklarationen, also die Vermeidung von Header-Abhängigkeiten. Ist ja in großen Projekten durchaus ein Punkt. Ich hatte bei meiner Frage nur an den ersten Anwendungsfall gedacht, dass also bestimmte Datenstrukturen einfach nicht formulierbar sind, wenn man nicht forward deklarieren kann.
Danke für das Beispiel.
Stefan.
-
DStefan schrieb:
Hmmm - ich verstehe. Das wäre dann der zweite Anwendungsfall von forward-Deklarationen, also die Vermeidung von Header-Abhängigkeiten. Ist ja in großen Projekten durchaus ein Punkt.
Ich setze Vorwärtsdeklarationen eigentlich meistens aus diesem Grund ein...
DStefan schrieb:
Ich hatte bei meiner Frage nur an den ersten Anwendungsfall gedacht, dass also bestimmte Datenstrukturen einfach nicht formulierbar sind, wenn man nicht forward deklarieren kann.
Jetzt bin ich mir nicht sicher, ob ich dich richtig verstanden habe. Meinst du, falls sich zwei Klassen gegenseitig benötigen (über Zeiger/Referenzen), dass du eine zuerst deklarieren musst?
-
Nexus schrieb:
DStefan schrieb:
Ich hatte bei meiner Frage nur an den ersten Anwendungsfall gedacht, dass also bestimmte Datenstrukturen einfach nicht formulierbar sind, wenn man nicht forward deklarieren kann.
Jetzt bin ich mir nicht sicher, ob ich dich richtig verstanden habe. Meinst du, falls sich zwei Klassen gegenseitig benötigen (über Zeiger/Referenzen), dass du eine zuerst deklarieren musst?
Genau das! Ich finde, diese Art der Verwendung ist das entscheidende Argument für die Nützlichkeit von forward-Deklarationen. Ohne diese bräuchte man sie nicht. Die Möglichkeit zur Verringerung von Header-Abhängigkeiten ist für mich nur ein Nebeneffekt (wenn auch ein durchaus nützlicher).
Stefan.