Problemlösung: C++ Templates (explizit)



  • Hallo liebe Community! 🙂

    Ich habe ein "Problem" mit einem meiner Templates... ich mache mal ein Kurzbeispiel bei dem der Fehler auftritt:

    Headerdatei rechenarten.h:

    #pragma once
    
    template <typename T> 
    T add(T const var_a, T const var_b);
    

    CPP-Datei rechenarten.cpp:

    #include "rechenarten.h"
    
    template <typename T> 
    T add(T const var_a, T const var_b)
    {
    	T rueckgabe;
    	rueckgabe = var_a + var_b;
    
    	return rueckgabe;
    }
    
    template double add<double>(double const var_a, double const var_b);
    

    (Kompiler ist Visual Studio Community 2017)

    Mein Programm kompiliert tadellos und die Funktionen lassen sich in der main auch aufrufen. Runtime ebenfalls Fehlerfrei! Egal ob für int oder double (int hab ich jetzt mal wegelassen ist aber analog). Gleiche Funktionen hab ich auch noch für sub, mult und div geschrieben (Grundrechenarten) bei denen alle das gleiche passiert.
    Dennoch unterkringelt mir VS den Funktionsnamen add in Zeile 12 grün mit dem Hinweis "Die Funktionsdefinition für "add" wurde nicht gefunden."

    Wenn ich auf die Lampe drücke und eine Definition von VS erstellen lasse spuckt er mir folgendes aus:

    template double add(double const var_a, double const var_b)
    {
    	return template double();
    }
    

    Hab ich hier irgendwo einen Fehler den ich einfach nicht bemerke oder verzettelt sich hier VS? Mein Code läuft zwar aber ich würde echt gerne wissen was hier vor sich geht und ob es an mir liegt oder an VS. 😔

    Vielen Dank schon mal für eure Hilfe! 😋



  • Templates gehören immer in die header files (gilt für implizit ... sieht Edit). Das liegt daran, dass sie eine Zwei Phasen Kompilierung haben. Zuerst wird das entsprechende Template auf Fehler (unabhängig vom Typ T) überprüft. Z.B Syntaxfehler etc. Wird die Methode irgenswo aufgerufen erfolgr eine Instantierung des Templates für einen bestimmten Typ (int, double etc.). Jetzt ist die zweite Phase, wo der Code noch al auf Fehler überprüft werden, die den Typen T betreffen. Z.b. benutzt du den Operator <, der ist für komplexe Typen ggf nichr definiert und würde in dem Fall in der zweiten Phase einen Fehler ausgeben. Der Compiler muss also zu einem späteren Zeitpunkt der Kompilierung nochmal die Definition der Methode sehen können, das funktioniert in dem klassischen Modell mit separierung in .cpp und .h nicht.

    Es gibt hier mehrere Lösungsansätze und keiner stellt die meisten zu 100% zufrieden, aber man kann damit leben. Mal zwei vorgestellt.

    1. Alles inline direkt in die header Datei. Daher Deklaration und Definition erfolgt beides in der Header Datei. Sollte es innerhalb einer Klasse sein, kannst du dir aussuchen, ob du es direkt in die Klasse schreibst oder unten drunter.

    2. Alle Definitionen in eine zweiter Header Datei packen, hier kann man z.B. die Endung .inl nehmen. In Rechenarten.h schreibst du ans Ende der Datei dann #include “Rechenarten.inl“.
      Wie du dir sicher denken kannst ist das letzendes das selbe wie 1., aber für dich und andere Entwickler ist es vlt. übersichtlicher.

    Edit: Wenn du magst kannst du trotzdem mal den Code in der main.cpp zeigen für die Aufrufe von add und den vollständigen Code für add (inklusive Spezialisierung für add).
    Aktuell sieht es nämlich so aus als hättest du Weg Nummer 3 für double benutzt. Nämlich konkret angegeben, für welche Datentypen du add benutzt (exlizit). Damir weiß der Compiler schon früher, dass er den Code für z.B. double checken muss. Nachteil ist natürlich ganz klar, dass du alle Typen auflisten musst, die du benutzen möchtest und das finde ich persönlich ätzend 😉



  • @Leon0402 sagte in Problemlösung: C++ Templates (explizit):

    Templates gehören immer in die header files

    Das ist falsch. So wie es oben steht kann man es machen. Ob es sinnvoll ist, ein template nur für double zu haben, steht auf einem anderen Blatt.



  • @Duffy sagte in Problemlösung: C++ Templates (explizit):

    oder verzettelt sich hier VS

    Ich sehe im Code keinen Fehler, also ja. Das letzte Wort hat der Compiler, nicht Intellisense.



  • @manni66 Das ist mir auch aufgefallen, habe ich auch in meinen edit geschrieben, falls du den gelesen hast. Interessieren würde mich nur mal, was man für Vorteile davon hat.

    Die explicit variante ist aber soweit richtig. G++ gibt auch keine Fehler/Warnings aus (mit -Wall, -Wextra)



  • @Leon0402 sagte in Problemlösung: C++ Templates (explizit):

    Templates gehören immer in die header files.

    -> Unterschied zwischen impliziter und expliziter Verwendung von Templates einmal anschauen...
    Die explizite Verwendung ist deutlich sinnvoller wenn man in mehreren CPP Dateien die Funktionen benutzen will... So verhindert man problemlos Codedoppelung wenn man mal vergisst überall bis auf an einer Stelle "extern" zu verwenden. Vor allem muss man sich nicht darauf einigen wer denn jetzt in seine CPP dafür zu sorgen hat das Objektcode erzeugt wird, weil es die eigene CPP des Template macht. 👍🏻

    So habe ich es zumindest gelernt... Nehme gerne Korrektur oder Ergänzungen entgegen! 😉

    Wenns nur einen Programmierer gibt ist es bei kleineren Geschichten vlt. unproblematisch aber bei größeren Projekten oder mehreren Programmierern sehe ich hier klar den Vorteil bei der expliziten Methode. Einmal CPP und Header erstellt und danach muss man sich um nichts mehr kümmern und muss nur noch wie eine normale Funktion verwenden.

    @manni66 sagte in Problemlösung: C++ Templates (explizit):

    Ob es sinnvoll ist, ein template nur für double zu haben, steht auf einem anderen Blatt.

    Sollte nur ein Beispiel werden... auch wenn ich es für float, int, short etc. noch mache bleibt das Problem bestehen. Ich fand das Beispiel so schön kurz und knackig um das Problem zu zeigen. 👍🏻

    @manni66 sagte in Problemlösung: C++ Templates (explizit):

    Ich sehe im Code keinen Fehler, also ja. Das letzte Wort hat der Compiler, nicht Intellisense.

    Das beruhigt mich aufjedenfall...

    @Leon0402 sagte in Problemlösung: C++ Templates (explizit):

    Wenn du magst kannst du trotzdem mal den Code in der main.cpp zeigen für die Aufrufe von add und den vollständigen Code für add (inklusive Spezialisierung für add).

    #include <iostream>
    #include "rechenarten.h"
    
    
    int main()
    {
    	std::cout << add(5.5, 5.5) << std::endl;
    	std::cout << sub(5.5, 1.9) << std::endl;
    
    	system("pause");
    	return 0;
    }
    

    Gleiches hab ich auch für int ausprobiert (inklusive Instanziierung für int wie bei double in Zeile 12 vom Header - also analog).

    Ich hab die ganze Geschichte jetzt noch einmal implizit gemacht nur mit main und der rechenarten.h und hier wird nirgendwo gemeckert (Rumpf mit in die Header gepackt).


    Danke für all eure Antworten!


Anmelden zum Antworten