Templatespezialisierung und Übernahme von Methoden



  • Ich dachte immer eine Templatespezialisierung übernimmt die Methoden und Variablen in der allgemeinen Definition der Template-Klasse, aber anscheinend ist das nicht so.

    Folgendes funktioniert nicht (mit vc7.1):

    template< int i >
    struct Vector
    {
      void print( ostream& out )
      {
         for( int j = 0; j < i; ++j )
            out << m[ j ] << "\t";
      }
      double m[ i ];
    };
    
    template<>
    struct Vector< 3 >
    {
      void crossP( const Vector< 3 >& other )
      {
     	m[ 0 ] = m[ 1 ] * other.m[ 2 ] - m[ 2 ] * other.m[ 1 ];
    	m[ 1 ] = m[ 2 ] * other.m[ 0 ] - m[ 0 ] * other.m[ 2 ];
    	m[ 2 ] = m[ 0 ] * other.m[ 1 ] - m[ 1 ] * other.m[ 0 ];
      }
    
      //ohne das findet er m in vector<3> nicht
      double m[ 3 ];
    };
    
    vector< 3 > vec1, vec2;
    
    vec1.crossP( vec2 ); //geht
    
    vec1.print( cout ); //geht nicht, er findet print nicht
    

    Das Beispiel soll nur das Problem wiedergeben.

    Gibt es keine Möglichkeiten in der Spezialisierung die Methoden und Variablen aus der allgemeinen Definition zu übernehmen?
    Und wie steht es mit Überladungen in der Spezialisierung?



  • 😮 ...



  • Nein, eine Spezialisierung übernimmt nichts von der allgemeinen Klasse - schließlich könnte sich ja die Struktur der Daten verändert haben (was soll print() machen, wenn Vector<3> statt des Arrays drei einzelne double-Variablen verwendet?)

    Du könntest höchstens eine Basisklasse definieren, die alle gemeinsamen Methoden enthält:

    template<int s>
    class Vec_base
    {
    public:
      ...
    };
    
    template<int s>
    class Vector : public Vec_base<s> {};
    
    template<>
    class Vector<3> : public Vec_Base<3>
    {
    public:
      void crossP(const Vector<3>& other) {...}
      ...
    };
    

  • Mod

    es spricht nichts dagegen, diese funktion bereits im primären template zu deklarieren. sie muss lediglich so geschrieben werden, dass der versuch, sie für andere template parameter zu instantiieren, fehlschlagen muss. z.b.:

    template< int i >
    struct Vector
    {
      void print( ostream& out )
      {
         for( int j = 0; j < i; ++j )
            out << m[ j ] << "\t";
      }
      void crossP( const Vector& other )
      {
         1 / ( i == 3 );
         m[ 0 ] = m[ 1 ] * other.m[ 2 ] - m[ 2 ] * other.m[ 1 ];
         m[ 1 ] = m[ 2 ] * other.m[ 0 ] - m[ 0 ] * other.m[ 2 ];
         m[ 2 ] = m[ 0 ] * other.m[ 1 ] - m[ 1 ] * other.m[ 0 ];
      }
      double m[ i ];
    };
    

    dieses vorgehen hat allerdings den nachteil, dass der fehler an der logisch falschen stelle entsteht, nähmlich innerhalb der funktion, und nicht dort, wo fälschlicherweise versucht wird, die funktion mit dem falschen parametern zu instantiieren. ebenso verhindert diese methode das explizite instatntiieren des templates. ein ausweg daraus bietet SFINAE, allerdings müssen wir die funktion dafür in ein template wandeln, also noch irgendwie einen templateparameter einführen und zwar so, dass er aus den funktionsargumenten ermittelt werden kann:

    template< bool b > struct enable_if {};
    template<> struct enable_if< true > { typedef void type; };
    template< int i >
    struct Vector
    {
      void print( ostream& out )
      {
         for( int j = 0; j < i; ++j )
            out << m[ j ] << "\t";
      }
      template< int j >
      void crossP( const Vector< j >& other, typename enable_if< i == 3 && j == 3 >::type* = 0 )
      {
         m[ 0 ] = m[ 1 ] * other.m[ 2 ] - m[ 2 ] * other.m[ 1 ];
         m[ 1 ] = m[ 2 ] * other.m[ 0 ] - m[ 0 ] * other.m[ 2 ];
         m[ 2 ] = m[ 0 ] * other.m[ 1 ] - m[ 1 ] * other.m[ 0 ];
      }
      double m[ i ];
    };
    

    mit boost würde das enable_if_c sein.



  • Ah Danke, auf SFINAE bin ich gar nicht gekommen, aber das ist für mein Fall genau das was ich brauche, danke 🙂


Log in to reply