TemplateFunktion in vererbter Klasse -> error LNK2019
-
Hi Community,
ich bin neu hier und hab ein Linker-Problem.
Vielleicht weiß von euch jemand wo der Hund begraben liegt.Ich habe abstrahiert (so auch getestet) folgendes Szenario:
main.cpp:
#include "child_class.h" void main(){ child_class a; a.virtualFunction(); }
base_class.h:
#pragma once #include <iostream> class base_class { public: base_class() = default; ~base_class() = default; base_class(base_class&) = delete; base_class(base_class&&) = delete; virtual void virtualFunction()=0; protected: template<typename pattern> void templateFunction(); };
base_class.cpp:
#include "base_class.h" template<typename pattern> void base_class::templateFunction() { pattern a = 3; std::cout << a << std::endl; }
child_class.h:
#include "base_class.h" class child_class : public base_class { public: child_class() = default; ~child_class() = default; child_class(child_class&) = delete; child_class(child_class&&) = delete; void virtualFunction()override; };
child_class.cpp:
#include "child_class.h" void child_class::virtualFunction() { templateFunction<int>(); }
CMakeList.txt:
cmake_minimum_required(VERSION 3.21) project(LearningProject) set(CMAKE_CXX_STANDARD 20) add_executable(LearningProject main.cpp)
Mein Problem ist nun, dass der Linker folgenden Fehler wirft:
main.cpp.obj : error LNK2019: Verweis auf nicht aufgel”stes externes Symbol ""public: virtual void __cdecl child_class::virtualFunction(void)" (?virtualFunction@child_class@@UEAAXXZ)" in Funktion "main".
Ich hab schon echt viel probiert, aber es will einfach nicht funktionieren.
Es geht, wenn ich die "templateFunction" ohne Template nutze, oder wenn ich in "child_class.cpp" ein "#include base_class.cpp" mache, aber das ist nicht wirklich eine Lösung, da es zukünftig mehere child Klassen geben wird.
Nun hoffe ich, dass irgendjemand die (vermutlich einfache) Lösung kennt.
Viele Grüße
Robert
-
@rohe sagte in TemplateFunktion in vererbter Klasse -> error LNK2019:
Nun hoffe ich, dass irgendjemand die (vermutlich einfache) Lösung kennt.
Hallo,
die einfache Antwort ist template-Funktionen im Header zu definieren.
Mehr Details z.B. hier
-
Solange man keine Module verwendet (aktuell noch ziemlich instabil das ganze) muss man zwingend Template in jeder Übersetzungseinheit sichtbar machen sprich über ein inklude einbinden.
-
@Jockelx Alles im Header zu implementieren fällt eig. raus, weil dann der Code überhaupt keine Struktur mehr hat und es handelt sich hier um sehr viele Funktionen mit sehr viel Code.
Ich hab auch mal weitergelesen und es mit "extern template" versucht, aber bis jetzt leider noch ohne Erfolg.
-
@john-0 Du meinst, dass ich die base_class.cpp in der child_class.cpp einbinden muss?
-
@rohe sagte in TemplateFunktion in vererbter Klasse -> error LNK2019:
Alles im Header zu implementieren fällt eig. raus
Nee, das fällt nicht raus, wenn du Standard-konformen Code schreiben willst.
Alles andere ist irgendein Gefrickel von Compilerherstellern oder ggf >= C++20. Laut @john-0 funktioniert das aber nicht gut. Keine Ahnung, das weiß ich nicht.Was du machen kannst und was auch jeder versteht: Du packst die Implementierungen in eine eigene Datei (*.tpp oder *.impl) und die inkludierst du dann am Ende deiner Header-Datei. Dann ist deine Header-Datei auch aufgeräumt.
-
@rohe sagte in TemplateFunktion in vererbter Klasse -> error LNK2019:
...
Alles im Header zu implementieren fällt eig. raus, weil dann der Code überhaupt keine Struktur mehr hat und es handelt sich hier um sehr viele Funktionen mit sehr viel Code.
...Man kann in templates auch Funktionen aufrufen, die nicht-templated und damit in einer .cpp Datei implementiert sind. Ich könnte jetzt sagen, dass Code dadurch strukturierter wird, weil die Zuständigkeiten besser verteilt sind. Aber dazu kenne ich deinen Code nicht, und deswegen sage ich das auch nicht.
-
@rohe sagte in TemplateFunktion in vererbter Klasse -> error LNK2019:
@john-0 Du meinst, dass ich die base_class.cpp in der child_class.cpp einbinden muss?
Grundsätzlich definiert man ein Template in einer Inklude Datei (die Dateiendung ist hier egal), außer Template Spezialisierungen die kommen in Implemtierungsdatei (.C .cc .cpp sind übliche Dateiendungen).
Sinnvoll ist es daher bei Dir gar kein base_class.cpp zu nehmen, sondern
base_class.h#ifndef BASE_CLASS_H #define BASE_CLASS_H #include <iostream> class base_class { public: base_class() = default; ~base_class() = default; base_class(base_class&) = delete; base_class(base_class&&) = delete; virtual void virtualFunction()=0; protected: template<typename pattern> void templateFunction() { pattern a = 3; std::cout << a << std::endl; }; }; #endif
zu schreiben.
-
@Jockelx sagte in TemplateFunktion in vererbter Klasse -> error LNK2019:
Laut @john-0 funktioniert das aber nicht gut. Keine Ahnung, das weiß ich nicht.
Module funktionieren in C++20 sowohl mit clang 14, GCC 11.x wie auch GCC 12.0 nicht wirklich. Wenn man von einigen trivial Beispiel absieht gibt es früher oder später ein Compilerfehler und die Übersetzung bricht ab.
-
Ok schon mal vielen Dank euch!
Es ist wohl doch nicht so einfach, wie ich dachte. Ich werd mal überlegen, wie ich das mit einer include-Datei sauber strukturieren kann.
Muss aber leider auf morgen warten, da noch ein paar Akutfälle reingekommen sind. Berichte dann aber.
-
So jetzt hab ich doch nochmal mit "extern template" herumprobiert und eine Lösung gefunden, die für mich voll funktioniert.
main.cpp:
#include "child_class.h" int main() { child_class a; a.virtualFunction(); }
base_class.h:
#pragma once #include <iostream> class base_class { public: base_class() = default; ~base_class() = default; base_class(base_class&) = delete; base_class(base_class&&) = delete; virtual void virtualFunction()=0; protected: template<typename T> void templateFunction(bool b); };
base_class.cpp:
#include "base_class.h" template<typename T> void base_class::templateFunction(bool b) { T a = 3; std::cout << a << " and " << b << std::endl; } template void base_class::templateFunction<int>(bool b);
child_clas.h:
#include "base_class.h" class child_class : public base_class { public: child_class() = default; ~child_class() = default; child_class(child_class&) = delete; child_class(child_class&&) = delete; void virtualFunction()override; };
child_class.cpp:
#include "child_class.h" extern template void base_class::templateFunction<int>(bool b); void child_class::virtualFunction() { base_class::templateFunction<int>(true); }
CMakeList.txt:
cmake_minimum_required(VERSION 3.21) project(LearningProject) set(CMAKE_CXX_STANDARD 20) add_executable(LearningProject main.cpp base_class.cpp child_class.cpp)
Entscheidend ist die Instanzierung in der base_class.cpp.
Nachdem was ich so gelesen hab, brauch ich die "extern"-Angabe in der child_class.cpp, damit das Object nicht doppelt erstellt wird (Kompilieren und Linken tuts auch ohne).
-
@rohe sagte in TemplateFunktion in vererbter Klasse -> error LNK2019:
Nachdem was ich so gelesen hab, brauch ich die "extern"-Angabe in der child_class.cpp, damit das Object nicht doppelt erstellt wird (Kompilieren und Linken tuts auch ohne).
Wenn die Implementierung des Templates nur in
base_class.cpp
verfügbar ist, dann brauchst du die "extern" Zeile gar nicht.
Und ansonsten gehört die "extern" Zeile dahin wo das Template implementiert wird.
-
Ok, top. Danke.