Problem mit Template (Zeiger und Wertetypen)



  • Hallo zusammen
    Ich zerbreche mir im Moment den Kopf bezüglich Templates und zwar habe ich folgende Situation. Man kann ja ein Template generell mit einem Zeigertyp oder einem Wertetyp generieren:

    MyMethod<int>(a,b);
    MyMethod<int*>(a,b);

    Das Problem ist nun, dass ich innerhalb des Template bestimmte Dinge mit den Daten macht, welche eine Unterscheidung zwischen Wertetyp sowie Zeigertyp erforderlich macht. Beispiel:

    template<typename T> MyMethod(T t1,T t2){
     if(t1 < t2) usw...
    }
    

    Nun ist sofort ersichtlich, dass dies mit einem Zeigertyp nicht funktionieren wird.

    template<typename T> MyMethod(T t1,T t2){
     if(*t1 < *t2) usw...
    }
    

    Dies funktioniert nun nicht mehr mit einem Wertetyp. Oder naja, vielleicht funktioniert es, weil gewisse Compiler soviel ich weis die Dereferenzierung eines Wertetyps ignorieren. Aber dann hat man das Problem immer noch sobald ich so etwas mache:

    MyMethod<int**>(a,b);

    Oder was ist, wenn ich beispielsweise irgendwo ein delete habe?

    template<typename T> MyMethod(T t1,T t2){
     delete t1;
    }
    

    Könnte man vielleicht noch so umgehen für Wertetypen:

    template<typename T> MyMethod(T t1,T t2){
     delete reinterpret_cast<T*>(t1);
    }
    

    Ist halt extrem gefährlich und darf dann mit Wertetypen niemals aufgerufen werden, aber immerhin ist es kompilierbar. (Macht hier natürlich keinen Sinn, aber vielleicht ist diese Methode innerhalb einer Klasse).

    Meine Beispiele sind sicherlich nicht besonders geschickt gewählt, doch für die Problematik aufzuzeigen vielleicht doch gar nicht so schlecht?

    Mfg Samuel



  • Du könntest eine Spezialisierung des Templates für Zeigertypen machen.
    Etwa so:

    template<typename T> MyMethod(T *t1,T *t2){
     if(*t1 < *t2) usw...
    }
    


  • OK, und der Compiler nimmt dann automatisch die korrekte Version?



  • Ishildur schrieb:

    OK, und der Compiler nimmt dann automatisch die korrekte Version?

    Ja.



  • Ich kann mir das Ganze irgendwie noch nicht so ganz vorstellen. Könntest du mir ein kleines Beispiel geben:

    Mit einer Klasse mit sagen wir 3 Methoden, von welchen 1 spezialisert für Zeigertypen sein muss.



  • Ishildur schrieb:

    Das Problem ist nun, dass ich innerhalb des Template bestimmte Dinge mit den Daten macht, welche eine Unterscheidung zwischen Wertetyp sowie Zeigertyp erforderlich macht.

    Warum? Klingt nach einem schlechten Design.

    Ishildur schrieb:

    Oder naja, vielleicht funktioniert es, weil gewisse Compiler soviel ich weis die Dereferenzierung eines Wertetyps ignorieren.

    Kann man nicht erwarten, nein. Ein solches Programm ist einfach nicht gültig.

    Ishildur schrieb:

    Aber dann hat man das Problem immer noch sobald ich so etwas mache:

    MyMethod<int**>(a,b);

    Ja und? Was soll denn dann passieren?

    Ishildur schrieb:

    Oder was ist, wenn ich beispielsweise irgendwo ein delete habe?

    template<typename T> MyMethod(T t1,T t2){
    delete t1;
    }
    

    Könnte man vielleicht noch so umgehen für Wertetypen:

    template<typename T> MyMethod(T t1,T t2){
     delete reinterpret_cast<T*>(t1);
    }
    

    Ganz großer Käse das. Kling nach einem schlechten Design.

    Ishildur schrieb:

    Meine Beispiele sind sicherlich nicht besonders geschickt gewählt, doch für die Problematik aufzuzeigen vielleicht doch gar nicht so schlecht?

    Hmmm... Der Ansatz ist komisch. In der Regel hat man bei Templates mit Typ-Parametern zu tun, die einem bestimmten (zu dokumentierenden) Konzept entsprechen. Zb das Konzept "Zeigerartig" oder "Werttyp". Natürlich kann man einen Zeiger wie einen "Werttyp" behandeln, Du kannst ja auch einen Vektor aus Zeigern anlegen. Den Vektor interessiert dann aber nicht, ob man auf den Elementen auch delete aufrufen kann. Wenn Du konzeptionell unterschiedliche Dinge machen willst, warum dann nicht einfach mehrere Funktionen bzw Funktionstemplates mit unterschiedlichen Namen dafür definieren?

    Erzähl doch mal, wo das Problem wirklich liegt. Warum denkst Du, so eine Sonderbehandlung zu benötigen? Was ist das für ein komisches Design?

    Gruß,
    SP



  • XMaster schrieb:

    Du könntest eine Spezialisierung des Templates für Zeigertypen machen.
    Etwa so:

    template<typename T> MyMethod(T *t1,T *t2){
     if(*t1 < *t2) usw...
    }
    

    Das ist keine Spezialisierung. Das ist ein neues Funktionstemplate (Überladung). Ist aber auch eigentlich besser so (meist). Partielle Spezialisierungen von Funktionstemplates gibt's auch gar nicht.

    Gruß,
    SP



  • Ok, ein kurzen Beispiel:

    #include <iostream>
    using namespace std;
    
    // Template-Methode
    template<class T> void F(T x)
    {
        cout << x << endl;
    }
    
    // Spezialisierung
    template<class T> void F(T *x)
    {
    	cout << *x << endl;
    }
    
    int main()
    {
    	int i = 3;
    	int *pi = &i;
    
    	F(i);   // Ausgabe: 3
    	F(pi);  // Ausgabe: auch 3 (!) - hier wird die Spezialisierung aufgerufen
    
    	return 0;
    }
    

    Das funktioniert aber nur, wenn die Variablen direkt Pointer auf Werte sind. Wenn du jetzt noch einen Pointer int **ppi = π hättest und dieses F übergibst, würde zwar auch die Spezialisierung aufgerufen werden, diese würde aber *ppi ausgeben, also pi und nicht i, was **ppi wäre.

    EDIT:

    Das ist keine Spezialisierung. Das ist ein neues Funktionstemplate (Überladung). Ist aber auch eigentlich besser so (meist). Partielle Spezialisierungen von Funktionstemplates gibt's auch gar nicht.

    Hm, da hab ich mich wohl mit der Terminologie verhauen :D. Aber gut, wie das am Ende heißt, ist für den Sachverhalt ja auch relativ egal.



  • XMaster schrieb:

    Ok, ein kurzen Beispiel:

    #include <iostream>
    using namespace std;
    
    // Template-Methode
    template<class T> void F(T x)
    {
        cout << x << endl;
    }
    
    // Spezialisierung
    template<class T> void F(T *x)
    {
    	cout << *x << endl;
    }
    
    int main()
    {
    	int i = 3;
    	int *pi = &i;
    
    	F(i);   // Ausgabe: 3
    	F(pi);  // Ausgabe: auch 3 (!) - hier wird die Spezialisierung aufgerufen
    
    	return 0;
    }
    

    Das hier ist ein Beispiel für den Missbrauch der Überladung. Diese zwei Funktions-Templates machen konzeptionell etwas anders. Das eine gibt das Objekt direkt aus, das andere dereferenziert vorher. Warum haben diese Funktions-Templates den gleichen Namen wenn sie von der Idee her etwas anderes machen?

    Hm, da hab ich mich wohl mit der Terminologie verhauen :D. Aber gut, wie das am Ende heißt, ist für den Sachverhalt ja auch relativ egal.

    Egal? Nein. "Spezialisierung" gibt es auch, auch für Funktions-Templates. Das macht man aber anders und ist meist auch eine schlechte Idee (bei Funktionen).

    Gruß,
    SP


Anmelden zum Antworten