Klassen Template Spezialisierung fuer Pointer
-
Hallo,
derzeit werden im Projekt geometrische Punkte und Mesh-Knoten in Vektoren auf verschiedene Art und Weisen verwaltet:
// case A: pointer to GeoLib::Point std::vector<GeoLib::Point*> p_points; // case B: pure GeoLib::Point std::vector<GeoLib::Point> points; // case A: pointer to MeshLib::Node std::vector<MeshLib::Node*> p_nodes; // case B: pure MeshLib::Node std::vector<MeshLib::Node> nodes;Um die Suche nach Punkten bzw. Mesh-Knoten effizient zu gestalten, soll das Klassen Template Grid verwendet werden. Das Grid verwaltet die Punkte/ Mesh-Knoten in einem Array von Vektoren:
std::vector<POINT*>* _grid_cell_to_node_map;In den Vektoren des Arrays werden Pointer auf Punkte/Mesh-Knoten gespeichert die zu einer entsprechenden Hexaeder-Zelle gehoeren.
template <typename POINT> class Grid { public: Grid(std::vector<POINT> const& pnts, size_t max_num_per_grid_cell = 512); // some template type independend methods private: std::vector<POINT*>* _grid_cell_to_node_map; };Fuer die Faelle (s.o. case A), das dem Konstruktor ein vector<PointerType> uebergeben wird habe ich eine Spezialisierung geschrieben.
template <typename POINT> class Grid<POINT*> { public: Grid(std::vector<POINT*> const& pnts, size_t max_num_per_grid_cell = 512); // copy of template type independend methods private: std::vector<POINT*>* _grid_cell_to_node_map; };Nun enthaelt mein Klassen Template aber recht viel Quelltext der eigentlich nicht davon abhaengig ist ob der zur Konstruktion des Objekts uebergebene Vektor Pointer-Typen oder Wert-Typen enthaelt. Eigentlich soll lediglich der Objekt-Konstruktions-Prozess modifiziert werden. Die restliche Funktionalitaet der Klasse ist unabhaengig ob Pointer- oder Wert-Typ uebergeben werden. Gibt es da eine elegante Loesung, ohne die meiste Funktionalitatet zu kopieren?
-
Ist nur der Konstruktorrumpf verschieden? Dann würde ich dort eine Funktion aufrufen, die für
TundT*überladen ist.Warum speicherst du eigentlich den
std::vectorselbst als Zeiger? Willst du ihn nicht automatisch verwalten lassen, d.h.std::vector<POINT*> _grid_cell_to_node_map;
-
Hallo Nexus,
danke fuer die schnelle Antwort. Im Prinzip ist nur der Konstruktor verschieden.
Ich habe auch schon Folgendes versucht:template <typename POINT> class Grid { public: // constructor 1 Grid(std::vector<POINT> const& pnts, size_t max_num_per_grid_cell = 512); // constructor 2 Grid(std::vector<POINT*> const& pnts, size_t max_num_per_grid_cell = 512); // some template type independend methods private: std::vector<POINT*>* _grid_cell_to_node_map; };Schreibe ich
std::vector<Node*> nodes; // fill the vector GeoLib::Grid<Node*>* grid(new GeoLib::Grid<Node*>(nodes, 64));wird nicht Konstruktor 2 ausgewaehlt, wie von mir erwuenscht, sondern Konstruktor 1 was (logischerweise) zu Kompilierproblemen fuehrt.
Zu deiner zweiten Frage warum ich den vector als Zeiger abspeichere. Dies ist eine Array von Vektoren. Jeder Vektor in diesem Array korrespondiert zu einer Grid-Zelle. Die Anzahl der Grid-Zellen lege ich einmal im Konstruktor fest und die Anzahl aendert sich auch nicht mehr. Deswegen habe ich ein Array genommen.
-
da du doch aber vectoren kennst, kannst du dort auch einen benutzen?
Bei deinem Template übergibst du Node* als typename. Damit hast du anschließendGrid(std::vector<Node*> const& pnts, size_t max_num_per_grid_cell = 512); Grid(std::vector<Node**> const& pnts, size_t max_num_per_grid_cell = 512);und das ist ja nicht das, was du möchtest.
greetz KN4CK3R
-
123_tom_dabei schrieb:
Ich habe auch schon Folgendes versucht
Versuch mal den erwähnten Ansatz mit Überladung.
123_tom_dabei schrieb:
Zu deiner zweiten Frage warum ich den vector als Zeiger abspeichere. Dies ist eine Array von Vektoren. Jeder Vektor in diesem Array korrespondiert zu einer Grid-Zelle. Die Anzahl der Grid-Zellen lege ich einmal im Konstruktor fest und die Anzahl aendert sich auch nicht mehr. Deswegen habe ich ein Array genommen.
Dann nimm bitte auch ein Array, sodass du Speicher nicht selbst verwalten musst. Falls du die Grösse zur Kompilierzeit weisst:
std::array< std::vector<POINT*>, groesse > _grid_cell_to_node_map;Falls die Grösse dynamisch ist, verschachtle zwei Vectors:
std::vector< std::vector<POINT*> > _grid_cell_to_node_map;Auch beim Element
POINT*gilt: Wenn die Zeiger nur Verweise sind, ist alles ok. Wenn du abernewunddeleteanwendest, machst du dir das Leben unnötig schwer. Manuelle Speicherverwaltung sollte wann immer möglich vermieden werden.
-
SFINAE könnte weiterhelfen
#include <type_traits> template <typename POINT> class Grid { public: // constructor 1 template <typename T, typename = typename std::enable_if<std::is_same<T, POINT>::value && !std::is_pointer<T>::value>::type> Grid(std::vector<T> const& pnts, size_t max_num_per_grid_cell = 512); // constructor 2 template <typename T, typename = typename std::enable_if<std::is_same<T, POINT>::value && std::is_pointer<T>::value>::type> Grid(std::vector<T> const& pnts, size_t max_num_per_grid_cell = 512); // some template type independend methods private: std::vector<typename std::add_pointer<typename std::remove_pointer<POINT>::type>::type>* _grid_cell_to_node_map; };
-
@ KN4CK3R
Okay, die Frage ob Array von Vektoren oder Vektor von Vektoren ist sicher ein bedenkenswerter Aspekt.Aber mein Hauptproblem ist ja ein anderes. Ich moechte das Klassen Template sowohl fuer
vector<PointerToPointTyp>als auch fuer
vector<PointTyp>benutzen ohne die nicht Pointer- bzw. Wert-Typ abhaengige Funktionalitaet der Klasse kopieren zu muessen. Im Prinzip brauche ich so eine Art
if (PointerTyp) use constructor 2 else use constructor 1oder eine Moeglichkeit bestimmte Methoden aus dem "unspezialisierten" Klassen Template zu uebernehmen.
-
Hallo camper,
camper schrieb:
SFINAE könnte weiterhelfen
Das klingt irgendwie nach einer Loesung. Leider bekomme ich einen Compile-Fehler
error: ‘template<class POINT> template<class T, class> GeoLib::Grid::Grid(const std::vector<T>&, size_t)’ cannot be overloaded
fuer Konstruktor 2.
camper schrieb:
// constructor 2 template <typename T, typename = typename std::enable_if<std::is_same<T, POINT>::value && std::is_pointer<T>::value>::type> Grid(std::vector<T> const& pnts, size_t max_num_per_grid_cell = 512);Ich wuerde die Konstrukte auch gerne verstehen. Gibt es da Links oder Artikel in denen die Konstrukte erklaert werden? Wozu wird zum Beispiel std::is_same<T, POINT> verwendet? Warum reicht es nicht aus std::is_pointer<...> zu verwenden. Das ist mir alles nicht richtig klar.
-
123_tom_dabei schrieb:
Hallo camper,
camper schrieb:
SFINAE könnte weiterhelfen
Das klingt irgendwie nach einer Loesung. Leider bekomme ich einen Compile-Fehler
error: ‘template<class POINT> template<class T, class> GeoLib::Grid::Grid(const std::vector<T>&, size_t)’ cannot be overloaded
Ja, der Fehler passiert mir fast immer, wenn ich so etwas schreibe...
Damit zwei Funktionstemplates überladen werden können, müssen sie sich in ihren Funktionsargumenten oder in der Templateargumentliste unterscheiden, das ist hier aber nicht der Fall (Default-Templateargumente zählen nicht). Das kann man durch Hinzufügen eines weiteren Dummy-Parameters zu einer Überladung lösen, oder indem der kein Typ-Parameter verwendet wird// constructor 1 template <typename T, typename std::enable_if<std::is_same<T, POINT>::value && !std::is_pointer<T>::value, int>::type = 0> Grid(std::vector<T> const& pnts, size_t max_num_per_grid_cell = 512); // constructor 2 template <typename T, bool dummy = true, typename std::enable_if<std::is_same<T, POINT>::value && std::is_pointer<T>::value, int>::type = 0> Grid(std::vector<T> const& pnts, size_t max_num_per_grid_cell = 512);123_tom_dabei schrieb:
Ich wuerde die Konstrukte auch gerne verstehen. Gibt es da Links oder Artikel in denen die Konstrukte erklaert werden?
Artikel habe ich gerade nicht zur Hand, vielleicht will jemand es erklären? Sonst schreib ich später noch was dazu.
123_tom_dabei schrieb:
Wozu wird zum Beispiel std::is_same<T, POINT> verwendet? Warum reicht es nicht aus std::is_pointer<...> zu verwenden.
Du willst ja nur mit Vektoren arbeiten, die ein POINT enthalten, nicht irgendetwas anderes wie vector<list<complex<float>>> oder vector<void(*)()> oder so.
-
Niemand der etwas zu SFINAE schreiben will? Wikipedia enhält einen brauchbaren Eintrag, die Erklärung in boost ist auch ganz brauchbar, geht allerdings nicht auf erst in C++11 mögliche syntaktische Konstrukte ein.
In Hinblick auf die Ausgangsfrage lässt sich wahrscheinlich eine elegantere Lösung finden. Soweit ich das sehe, soll grid_cell_to_node_map am Ende entweder Verweise auf die Elemente des initialisierenden Containers (wenn es ein vector<T> war) oder auf die durch den initialisierenden Vector referenzierten Objekte (im Fall vector<T*>) enthalten. Dann lässt sich das Ganze stark vereinfachen, und eigentlich kommt es gar nicht darauf an, of die Elemente sich ursprünglich in einem vector befinden, ein Iteratorpaar genügt.
template <typename POINT> class Grid { private: static POINT* copy_or_address(POINT& p) { return &p; } static POINT* copy_or_address(POINT* p) { return p; } public: template <typename InputIterator> Grid(InputIterator first, InputIerator last, size_t max_num_per_grid_cell = 512); // überall einfach copy_or_address(*it) verwenden private: std::vector<POINT*> _grid_cell_to_node_map; };