templates und pointer-to-member-function in verteilten Quelldateien
-
Die Beispiele sind natürlich bewusst einfach konstruiert und drum sinnfrei!
Nehmen wir an, wir hätten ein algebraisches Konstrukt definiert, sagen wir einmal eine Ebene. Da wäre es doch schön, wenn dieselbe für mehrere Datentypen funktionieren würde, fixed-point Ganzzahlen, double, vielleicht auch für Exotischeres.
Da bietet es sich wohl an, das Ding als template zu definieren, mit dem numerischen Datentyp als Parameter.Nun kommt man vielleicht in die Verlegenheit, dass man zwar weiß, wie oft eine Methode einer Klasse ausgeführt werden soll - aber nicht welche. Na, da lässt man sich eben einfach sagen, welche es denn sein soll, per Parameter.
Kombiniert man nun diese beiden Lösungen, will man also einen Zeiger auf die Methode einer template-Ebene herumreichen, so trifft man - zumindest bei minGW (G++ 4.7.0) - auf folgendes Kuriosum:
Ist die Ebene in der selben Quelldatei definiert, funktioniert's - ist sie das aber nicht, findet der Linker die angegebenen Methoden nicht.
Definierte man die Ebene nun nicht als template - funktioniert es wiederum.Was wäre zu tun, um dieses Unbill zu beseitigen, mit anderen Worten: seine allgemeine Ebene andernortes zu definieren ohne deshalb nicht mehr ihre Methoden zeigerhaft herumreichen zu können?
#include <iostream> #include <valarray> using namespace std; template <typename T> class Ebene { protected: valarray<T> fusspunkt; valarray<T> normalenvektor; public: Ebene() : fusspunkt(), normalenvektor() {} Ebene(const valarray<T> & neuerFusspunkt) : fusspunkt(neuerFusspunkt), normalenvektor() {} Ebene(const valarray<T> & neuerFusspunkt, const valarray<T> & neuerNormalenvektor) : fusspunkt(neuerFusspunkt), normalenvektor(neuerNormalenvektor) {} Ebene(const Ebene<T> & ebene) : fusspunkt(ebene.fusspunkt), normalenvektor(ebene.normalenvektor) {} bool istOberhalb (const valarray<T> & punkt) // Punkt liegt in Richtung des Normalenvektors oder in der Ebene { return 0. >= (this->normalenvektor * (this->fusspunkt - punkt)).sum(); } bool istUnterhalb(const valarray<T> & punkt) // Punkt liegt in Richtung des invertierten Normalenvektors oder in der Ebene { return 0. <= (this->normalenvektor * (this->fusspunkt - punkt)).sum(); } }; bool punktLiegtGut(Ebene<double> & ebene, const valarray<double> & punkt, bool (Ebene<double>::* oberOderUnterhalb)(const valarray<double> & punkt)) { return (ebene.*oberOderUnterhalb)(punkt); } int main () { valarray<double> normale(3); valarray<double> fuss(3); valarray<double> punkt(3); for(unsigned int z = 0; z < 3; z++) { normale[z] = (double) z; fuss[z] = (double)-z; punkt[z] = (double)(z * (z % 2) - z * ((z + 1) % 2)); } Ebene<double> e(fuss, normale); cout << punktLiegtGut(e, punkt, &Ebene<double>::istOberhalb) << endl; cout << punktLiegtGut(e, punkt, &Ebene<double>::istUnterhalb) << endl; return 0; }/************/ /* main.cpp */ /************/ #include <iostream> #include <valarray> #include "Include.h" using namespace std; bool punktLiegtGut(Ebene<double> & ebene, const valarray<double> & punkt, bool (Ebene<double>::* oberOderUnterhalb)(const valarray<double> & punkt)) { return (ebene.*oberOderUnterhalb)(punkt); } int main () { valarray<double> normale(3); valarray<double> fuss(3); valarray<double> punkt(3); for(unsigned int z = 0; z < 3; z++) { normale[z] = (double) z; fuss[z] = (double)-z; punkt[z] = (double)(z * (z % 2) - z * ((z + 1) % 2)); } Ebene<double> e(fuss, normale); cout << punktLiegtGut(e, punkt, &Ebene<double>::istOberhalb) << endl; cout << punktLiegtGut(e, punkt, &Ebene<double>::istUnterhalb) << endl; return 0; } /*************/ /* Include.h */ /*************/ #ifndef INCLUDE_H #define INCLUDE_H #include <valarray> using namespace std; template <typename T> class Ebene { protected: valarray<T> fusspunkt; valarray<T> normalenvektor; public: Ebene(); Ebene(const valarray<T> & neuerFusspunkt); Ebene(const valarray<T> & neuerFusspunkt, const valarray<T> & neuerNormalenvektor); Ebene(const Ebene<T> & ebene); bool istOberhalb (const valarray<T> & punkt); // Punkt liegt in Richtung des Normalenvektors oder in der Ebene bool istUnterhalb(const valarray<T> & punkt); // Punkt liegt in Richtung des invertierten Normalenvektors oder in der Ebene }; #endif /***************/ /* Include.cpp */ /***************/ #include "Include.h" template <typename T> Ebene<T>::Ebene() : fusspunkt(), normalenvektor() {} template <typename T> Ebene<T>::Ebene(const valarray<T> & neuerFusspunkt) : fusspunkt(neuerFusspunkt), normalenvektor() {} template <typename T> Ebene<T>::Ebene(const valarray<T> & neuerFusspunkt, const valarray<T> & neuerNormalenvektor) : fusspunkt(neuerFusspunkt), normalenvektor(neuerNormalenvektor) {} template <typename T> Ebene<T>::Ebene(const Ebene<T> & ebene) : fusspunkt(ebene.fusspunkt), normalenvektor(ebene.normalenvektor) {} template <typename T> bool Ebene<T>::istOberhalb(const valarray<T> & punkt) { return 0. >= (this->normalenvektor * (this->fusspunkt - punkt)).sum(); } template <typename T> bool Ebene<T>::istUnterhalb(const valarray<T> & punkt) { return 0. <= (this->normalenvektor * (this->fusspunkt - punkt)).sum(); }C:\Users\Zsar\AppData\Local\Temp\ccjsHpv2.o:Test.c:(.text+0x156): undefined reference to `Ebene<double>::Ebene(std::valarray<double> const&, std::valarray<double> const&)' C:\Users\Zsar\AppData\Local\Temp\ccjsHpv2.o:Test.c:(.text+0x160): undefined reference to `Ebene<double>::istOberhalb(std::valarray<double> const&)' C:\Users\Zsar\AppData\Local\Temp\ccjsHpv2.o:Test.c:(.text+0x1b0): undefined reference to `Ebene<double>::istUnterhalb(std::valarray<double> const&)' collect2.exe: error: ld returned 1 exit status
-
Templates muessen in die .hpp.
-
Hmm, das war ja... einfach. Darauf hätte ich in einem weniger dämlichen Zustande durchaus auch selbst kommen dürfen.
Ach weh, das erklärt dann wohl alles. Vielen Dank für den Denkersatz, ich wollte ihn am liebsten sogleich - fest installieren.