PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C++] Template Spezialisierung


Silpion
2007-02-14, 14:42:50
Hallo,

ich habe ein kleines Problem, für dass ich noch keine vernünftige Lösung gefunden habe:

Ich habe eine Klasse
template <class T>
class Foo
{
...
};
die sehr viele Funktionen für alle T enthält. Nun möchte ich noch eine (statische) Funktion hinzufügen, die für ein dutzend T etwas leicht anderes macht. Normale Template-Spezialisierung mit z.B.
template <>
class Foo<int>
{
...
};
führt dazu, dass ich die komplette Klasse naherzu identisch neu definieren muss, was Wartung des Codes extrem unhandlich werden lässt, vor allem für die anderen Projektmitarbeiter, die sich das nicht ausgedacht haben. Eine spezialisierte Template-Funktion in der Template-Klasse
template <class T>
class Foo
{
...
// Template für nicht weiter definierte Typen
template <class S>
static Foo<S> Bar(void)
{
printf("Error: Type not supported.");
}

// Für einzelne ausgewählte Typen
template <>
static Foo<int> Bar<int>(void)
{
...
}
};
funktioniert soweit, allerdings ist der Aufruf reichlich fies:
Foo<int> obj = Foo<int>::Bar<int>();
Klingt noch ok, aber
Foo<int> obj = Foo<unsigned char>::Bar<int>();
wäre das gleiche. Der erste Template-Parameter ist völlig unwichtig.

Gibt es hier eine bessere Lösung?

Kurze Zusammenfassung: Es existiert eine Template-Klasse mit sehr vielen Funktionen für alle Typen, eine Funktion soll hinzu die für einzelne Typen besonderes macht, ohne dass der Code schwierig im Bezug Wartung und Lesbarkeit wird.

PS: Kann man eigentlich irgendwie zur Laufzeit feststellen, ob eine Variablen von einem bestimmten Typ ist?

tokugawa
2007-02-14, 22:45:14
Wie wär's damit:


template <class T>
class Foo
{
...
};

class SpecializedFoo: public Foo<int>
{
blabla
};



Also die Spezialisierung über eine Ableitung?

So ähnlich funktionieren bei uns die Singletons z.B.



PS: Kann man eigentlich irgendwie zur Laufzeit feststellen, ob eine Variablen von einem bestimmten Typ ist?

Mit Runtime-Type-Information und dem typeid-Operator geht das. Obwohl man damit sich auch gehörig in den Fuß schießen kann (wie auch mit template-Spezialisierungen).

Corrail
2007-02-15, 09:43:34
Wie wär's damit:


template <class T>
class Foo
{
...
};

class SpecializedFoo: public Foo<int>
{
blabla
};



Also die Spezialisierung über eine Ableitung?

Das Problem hier wäre allerdings, dass SpecializedFoo nicht mehr als Foo gilt. Somit würden externe template-funktionen, die ein Foo erwarten nicht mehr mit SpecializedFoo funktionieren:


template<class T>
void do_something(const foo<T> & bla); // würde Specialized Foo nicht erkennen


Was eine (nicht unbedingt hübsche) Möglichkeit wäre, ist alle Memberfunktionen von der nicht spezialisierten Foo Klasse rauszuziehen und diese Funktionen werden dann in der Klasse aufgerufen. Damit ersparst du dir zwar kein Copy&Paste, aber der Code ist wartbarer:


anstatt

template<class T>
class foo
{
void bla()
{ ... }
};

template<>
class foo<int>
{
void bla()
{ ... } // gleiche implementierung wie oben
};


sowas:



template<class T>
class foo
{
void bla();
};

temaplte<>
class foo<int>
{
void bla();
};

template<class T>
void extern_bla(const foo<T> & x)
{ ... }

template<class T>
void foo<T>::bla()
{ extern_bla<T>(*this); }

template<>
void foo<int>::bla()
{ extern_bla<int>(*this); }


Ist zwar nicht elegant, aber du hast nur eine implementierung von bla, die sich ja auch nicht ändert.

Da fällt mir grad ein: braucht man die folgenden Implementierung überhaupt?

template<>
void foo<int>::bla()
{ extern_bla<int>(*this); }

wenn man die allgemeine schon hat?

Xmas
2007-02-15, 14:43:15
Das Problem hier wäre allerdings, dass SpecializedFoo nicht mehr als Foo gilt. Somit würden externe template-funktionen, die ein Foo erwarten nicht mehr mit SpecializedFoo funktionieren:


template<class T>
void do_something(const foo<T> & bla); // würde Specialized Foo nicht erkennen

Das verstehe ich jetzt nicht ganz. Sofern Referenzen oder Pointer übergeben werden, sollte das überhaupt kein Problem sein. Der folgende Code gibt wie erwartet 2 aus:
#include <iostream>

template <class T> class Foo
{
public:
virtual T value() { return (T)1; }
};

class SpecializedFoo : public Foo<int>
{
virtual int value() { return 2; }
};

template <class T> bar(Foo<T> & someFoo)
{
return someFoo.value();
}

int main()
{
SpecializedFoo foo;
std::cout << bar(foo);
}

Corrail
2007-02-15, 15:34:08
Das stimmt schon, nur läufst du hier jetzt in die Gefahr, dass der function call per Laufzeit ausgeführt wird und nicht zu Compilezeit, wo im besten Fall sogar geinlined wird. Im schlimmsten Fall hat man sogar einen virtual function lookup, virtueller Destructor ist Pflicht, ... Alles Sachen, die der Performance schaden für Code, die der schon zur Compilezeit ausgewertet und optimiert werden kann. Wenn das kein Thema ist, dann ist es natürlich kein Problem. Dann bleibt aber die Frage stehen, warum überhaupt Templates verwendet wurden.