[solved] template spezialisierung funktioniert nicht
-
hi zusammen!
ich hab folgendes problem (mit msvs2005+sp1): ich hab 'ne template methode in der klasse Logger
#ifndef __LOGCLIENT_H__ #define __LOGCLIENT_H__ class LogClient { //... template<class T> void LogClient::Log( T &t, LOGLEVEL logLevel ) { if( logLevel > this->logLevel ) return; if( !this->logger ) this->logger = Logger::GetSingleton(); this->logger->Log( t, this, logLevel ); }und davon 'ne spezialisierung für stringstreams:
template<> void LogClient::Log<std::stringstream>( std::stringstream &ss, LOGLEVEL logLevel ) { this->Log( ss.str(), logLevel ); } }; #endifdas funktioniert soweit, nur wenn ich das file in mehreren .cpps includiere schreit der linker ab dem zweiten file:
Linker schrieb:
Error 1 error LNK2005: "public: void __thiscall psi::LogClient::Log<class std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> > >(class std::basic_stringstream<char,struct std::char_traits<char>,class std::allocator<char> > &,enum psi::LOGLEVEL)" (??basic_stringstream@DU?allocator@D@2@@std@@@LogClient@psi@@QAEXAAV?char_traits@D@std@@V?$allocator@D@2@@std@@W4LOGLEVEL@1@@Z) already defined in Main.obj NetClient.obj
kann mir beim besten willen net erklären was ihm das nicht passt. HILFÄÄÄH!!

-
Eine explizite Spezialisierung eines Templates ist kein Template. Es gilt also die übliche ODR. Die Definition darf nur einmal existieren, oder du machst eine inline Funktion draus. Wenn du die Definition in einer .cpp Datei durchführst, musst du sie allerdings trotzdem im Header deklarieren - andernfalls würde der Compiler in allen anderen Übersetzungseinheiten das Primärtemplate anwenden.
-
asoooh! das wusst' ich nicht, danke...
camper schrieb:
musst du sie allerdings trotzdem im Header deklarieren
hm. wie schreibt ma das syntaktisch an? oO
-
iko79 schrieb:
asoooh! das wusst' ich nicht, danke...
camper schrieb:
musst du sie allerdings trotzdem im Header deklarieren
hm. wie schreibt ma das syntaktisch an? oO
Wie jede andere Funktionsdeklaration. Du lässt einfach den Funktionsrumpf weg, und schreibst ein Semikolon dafür

template<> void LogClient::Log<std::stringstream>( std::stringstream &ss, LOGLEVEL logLevel );
-
so gehts, ausserhalb der klasse, hast recht.
...hätte das nämlich innerhalb der klasse versucht:class LogClient { //... template<class T> void Log( T &t, LOGLEVEL logLevel ); template<> void Log<std::stringstream>( std::stringstream &ss, LOGLEVEL logLevel ); };und dann in der cpp implementiert:
template<> void LogClient::Log<std::stringstream>( std::stringstream &ss, LOGLEVEL logLevel ) { this->Log( ss.str(), logLevel ); }hast vielleicht da auch noch schnell a idee was daran falsch ist?
-
okay, scheinbar nicht...
danke trotzdem, hast mir sehr geholfen!
-
Was bekommst du denn für einen Fehler?
Ansich sollte das schon gehen...Das "LogClient::" in der Definition von LogClient selbst ist allerdings unnötig und AFAIK auch falsch... der MSVC frisst das nur "trotzdem" (auch mit ausgeschalteten Extensions!?!).
-
... sorry falscher thread

-
*edit*
okay, ich bin ein blödmann. was hier vorhin gestanden is ging nicht weil ich ein using namespace vergessen hab. ma muss ma aber zu gute halten: die fehlermeldungen vom compiler waren ja auch net ganz aufschlussreich...

jedoch hab i jetzt ein ganz anderes problem: i möcht eine template-klasse schreiben, von der der konstruktor und eine methode spezialisiert sind. diese spezialisierungen müssen in cpps ausgelagert werden, weils sonst linker-errors hagelt. soweit noch klar. weiters müssen die spezialisierungen im headerfile deklariert werden. auch logisch. nur gehts irgendwie nicht, jedenfalls krieg ichs nicht hin, die schreibweise hunzt. hab jetzt mehrere durchprobiert:
path.h
#ifndef __PATH_H__ #define __PATH_H__ #include <ogrequaternion.h> namespace psi { template <class T> class Path { public: virtual ~Path() {} virtual T Calc( float progress ) = 0; }; template <class T> class LinearPath : public Path<T> { private: T t0; T t1; public: LinearPath( T start, T end ) : t0( start ), t1( end - start ) {} T Calc( float progress ); template <> LinearPath( Ogre::Quaternion start, Ogre::Quaternion end ); template <> Ogre::Quaternion Calc( float progress ); }; template <class T> T LinearPath<T>::Calc( float progress ) { return ( this->t0 + this->t1 * progress ); } } #endifpath.cpp
#include "Path.h" using namespace psi; template <> LinearPath<Ogre::Quaternion>::LinearPath( Ogre::Quaternion start, Ogre::Quaternion end ) : t0( start ), t1( end ) {} template <> Ogre::Quaternion LinearPath<Ogre::Quaternion>::Calc( float progress ) { return Ogre::Quaternion::Slerp( progress, this->t0, this->t1, true ); }wenn ich path.h in zwei files includiere krieg ich nen linker error.
Error 1 error LNK2005: "public: virtual class Ogre::Quaternion __thiscall psi::LinearPath<class Ogre::Quaternion>::Calc(float)" (?Calc@?$LinearPath@VQuaternion@Ogre@@@psi@@UAE?AVQuaternion@Ogre@@M@Z) already defined in mainTest.obj Path.obj
kann ma wer sagen wie die syntax ausschauen muss dass sowas klappt?
-
Deklarier die Funktion als 'inline', das sollte funktionieren.
-
Das ist aber trotzdem noch nicht korrekt.
Der 2. Konstruktor ist eine normale Überladung:
LinearPath( Ogre::Quaternion start, Ogre::Quaternion end );Und du kannst keine 2 gleichen Funktionen mit unterschiedlichen Rückgabewerten deklarieren:
T Calc( float progress ); Ogre::Quaternion Calc( float progress );Welche soll er dann bei "t.Calc(0.0f)" aufrufen?
-
danke für die antworten. i hab jetzt inzwischen nach endlosem herumprobieren hinbekommen indem ich die gesamte klasse spezialisiert hab. habs anders nicht hinbekommen. i schreib das hier nur rein, damit ich - wenn ich 's nächste mal das selbe problem hab (und das passiert bestimmt) - die lösung find.
template <class T> class LinearPath : public Path<T> { private: T t0; T t1; public: LinearPath() { this->t0 = 0; this->t1 = 0; } LinearPath( T start, T end ) { this->Set( start, end ); } T Calc( float progress ); void Set( T start, T end ) { this->t0 = start; this->t1 = end - start; } T GetStart() { return ( this->t0 ); } T GetEnd() { return ( this->t0 + this->t1 ); } }; template <class T> T LinearPath<T>::Calc( float progress ) { return ( this->t0 + this->t1 * progress ); } template <> class LinearPath<Ogre::Quaternion> : public Path<Ogre::Quaternion> { private: Ogre::Quaternion t0; Ogre::Quaternion t1; public: LinearPath( Ogre::Quaternion start, Ogre::Quaternion end ) { this->Set( start, end ); } Ogre::Quaternion Calc( float progress ); void Set( Ogre::Quaternion start, Ogre::Quaternion end ) { this->t0 = start; this->t1 = end; } Ogre::Quaternion GetStart() { return this->t0; } Ogre::Quaternion GetEnd() { return this->t1; } };#include "Path.h" using namespace psi; Ogre::Quaternion LinearPath<Ogre::Quaternion>::Calc( float progress ) { return Ogre::Quaternion::Slerp( progress, this->t0, this->t1, true ); }Th schrieb:
Welche soll er dann bei "t.Calc(0.0f)" aufrufen?
das is ja ka überladung sondern a spezialisierung. er soll natürlich die methode verwenden, die für die aktuell verwendete klasse kompiliert worden ist -> also in dem fall
LinearPath<Ogre::Quaternion> path( q1, q2 ); q = path.Calc( 0.75f );eben die spezialisierte, in dem fall
LinearPath<int> path( 10, 120 ); i = path.Calc( 0.75f );eine für int kompilierte version.