Warum erhalte ich bei der Verwendung von Vorlagen die Fehlermeldung „Ungelöstes externes Symbol“? [duplicate]

Lesezeit: 6 Minuten

Warum erhalte ich bei der Verwendung von Vorlagen die Fehlermeldung
dlanod

Wenn ich C++-Code für eine Klasse unter Verwendung von Vorlagen schreibe und den Code zwischen einer Quelldatei (CPP) und einer Header-Datei (H) aufteile, erhalte ich beim Verknüpfen der endgültigen ausführbaren Datei eine ganze Reihe von „ungelösten externen Symbol“-Fehlern. obwohl die Objektdatei korrekt erstellt und in die Verknüpfung aufgenommen wurde. Was passiert hier und wie kann ich es beheben?

  • Siehe auch stackoverflow.com/questions/495021/…

    – etw

    7. September 2013 um 1:41 Uhr


Warum erhalte ich bei der Verwendung von Vorlagen die Fehlermeldung
dlanod

Template-Klassen und -Funktionen werden erst instanziiert, wenn sie verwendet werden, normalerweise in einer separaten .cpp-Datei (z. B. der Programmquelle). Wenn die Vorlage verwendet wird, benötigt der Compiler den vollständigen Code für diese Funktion, um die richtige Funktion mit dem entsprechenden Typ erstellen zu können. In diesem Fall ist der Code für diese Funktion jedoch in der Quelldatei der Vorlage detailliert und daher nicht verfügbar.

Infolgedessen geht der Compiler einfach davon aus, dass er an anderer Stelle definiert ist, und fügt nur den Aufruf der Vorlagenfunktion ein. Beim Kompilieren der Quelldatei der Vorlage wird der spezifische Vorlagentyp, der in der Programmquelle verwendet wird, dort nicht verwendet, sodass der für die Funktion erforderliche Code immer noch nicht generiert wird. Dies führt zu dem unaufgelösten externen Symbol.

Die dafür verfügbaren Lösungen sind:

  1. Fügen Sie die vollständige Definition der Member-Funktion in die Header-Datei der Vorlage ein und haben Sie keine Quelldatei für die Vorlage.
  2. Definieren Sie alle Elementfunktionen in der Quelldatei der Vorlage als “inline”. (Aktualisieren: [this does not work on Visual Studio 2017+]), oder
  3. Definieren Sie die Elementfunktionen in der Quelle der Vorlage mit dem Schlüsselwort “export”. Leider wird dies von vielen Compilern nicht unterstützt. (Aktualisieren: dies wurde ab C++11 aus dem Standard entfernt.)

Sowohl 1 als auch 2 gehen das Problem grundsätzlich an, indem sie dem Compiler Zugriff auf den vollständigen Code für die Schablonenfunktion gewähren, wenn er versucht, die typisierte Funktion in der Programmquelle zu erstellen.

  • Bei (3) hast du einen Tippfehler. Sie meinten wahrscheinlich Schlüsselwort und nicht Tastatur. Ich sehe nicht, wie das Definieren der Funktionen als “Inline” helfen wird. Sie müssen sie in den Header einfügen oder die Vorlagen explizit mit den benötigten Typen instanziieren.

    – nimrodm

    19. Januar 2009 um 6:53 Uhr

  • Sie sollten (2) möglicherweise umformulieren. keine ahnung was du damit meinst

    – Johannes Schaub – litb

    19. Januar 2009 um 7:23 Uhr

  • Das Schlüsselwort „export“ liefert ebenfalls die vollständige Definition. Es kann in einer leicht codierten Form vorliegen, wie ein Compiler-Parse-Baum, aber es ist nicht sehr gut versteckt. Natürlich nehme ich an, dass Maschinencode die Quelle auch nicht sehr gut verbirgt.

    – Zan Luchs

    19. Januar 2009 um 23:26 Uhr

  • Ich habe das gleiche Problem, und das beantwortet es nicht … Ich weiß nicht, warum dies als Antwort akzeptiert wurde. Das Einschließen der vollständigen Definition der Member-Funktionen funktioniert, aber meiner Meinung nach stellt es einen Mangel an Sicherheit für unsere Programme dar und im Allgemeinen eine schlechte Praxis, die zu unorganisiertem Code führt.

    – Viktor

    17. März 2014 um 8:30 Uhr

  • @JohannesSchaub-litb Wenn alles in der Header-Datei (Definition) sein muss, was nützt es dann .cpp Datei?

    – Benutzer786

    21. September 2016 um 10:11 Uhr

Eine andere Möglichkeit besteht darin, den Code in die cpp-Datei zu schreiben und in derselben cpp-Datei explizite Instanziierungen der Vorlage mit den Typen hinzuzufügen, die Sie voraussichtlich verwenden werden. Dies ist nützlich, wenn Sie wissen, dass Sie es nur für ein paar Typen verwenden werden, die Sie im Voraus kennen.

  • Also im Wesentlichen gesagt f*** *** u zu Modularität, Wiederverwendung, Einzelverantwortung und Trennung von Anliegen … und der springende Punkt der generischen Programmierung, die darin besteht, generische Klassen zu haben, die mit jedem gewünschten Typ verwendet werden können ohne dass die Template-Klasse es vorher weiß wofür soll es verwendet werden?

    – jbx

    31. Oktober 2015 um 15:51 Uhr


  • @jbx Ich sage das für Dinge wie basic_string<T> Sie werden es immer nur mit verwenden char oder wchar_t Wenn es also darum geht, die gesamte Implementierung in den Header zu packen, ist die Instanziierung im cpp eine Option. Der Code steht Ihnen zur Verfügung, nicht umgekehrt.

    – tschüss

    31. Oktober 2015 um 20:19 Uhr

  • Besiegt meiner Meinung nach den ganzen Sinn von Vorlagen. Ihr Beispiel ist nur eine Ausnahme (die wohl mit Überladung geschrieben werden sollte, wenn es nur für 2 Typen gilt, aber das ist ein anderes Argument). Bei der Template-Programmierung soll es darum gehen, etwas zu erstellen, das unabhängig von den Typen funktioniert, mit denen es interagiert, ohne es vorher zu wissen. Das Vorhersagen, was die Typen sein werden, geht gegen seinen ganzen Zweck. Es ist nur eine schlechte Praxis, die den Schmerz „löst“, aber nur weil Sie es können, heißt das nicht, dass Sie es sollten.

    – jbx

    31. Oktober 2015 um 20:48 Uhr

  • @jbx Falsch. Bei der Template-Programmierung geht es darum, sich nicht zu wiederholen. Wenn das, was Sie schreiben, super allgemein und wunderbar ist, gut für Sie, aber das ist keineswegs notwendig. Wenn es mir erlaubt, nur eine Klasse oder Funktion statt 2 zu schreiben, hat es seinen Zweck erfüllt. Auch die Standard-Container sind nicht unabhängig vom Typ, sie hängen von Dingen wie default c’tor und move c’tor ab.

    – tschüss

    1. November 2015 um 7:46 Uhr

  • Sie sagen, dass es nicht darum geht, sich nicht zu wiederholen, dann sagen Sie im nächsten Satz, dass es seinen Zweck erfüllt hat, wenn es Ihnen erlaubt, nur eine Klasse oder Funktion anstelle von 2 zu schreiben. Wieso also nicht, sich nicht zu wiederholen? Wie kann Ihr Code dem Open-Closed-Prinzip folgen, wenn Sie die Header-Datei Ihrer Vorlage jedes Mal ändern müssen, wenn Sie sie für einen anderen neuen Typ verwenden müssen. Lassen Sie uns zugeben, dass dies ein Hack aufgrund der Einschränkungen von C++ ist, einer der lästigen Designfehler wie viele andere.

    – jbx

    1. November 2015 um 13:43 Uhr


Für jede Datei, die die .h-Datei enthält, sollten Sie beide Zeilen einfügen:

#include "MyfileWithTemplatesDeclaration.h"
#include "MyfileWithTemplatesDefinition.cpp"

Probe

#include "list.h"
    #include "list.cpp" //<---for to fix bug link err 2019



    int main(int argc, _TCHAR* argv[])
    {
        list<int> my_list;
        my_list.add_end(3);
    .
    .
    } 

Außerdem vergessen Sie nicht, Ihre Deklarationsklasse unter Centinel-Konstanten zu platzieren

#ifndef LIST_H
#define LIST_H
#include <iostream>
.
.
template <class T>
class list
{
private:
    int m_size,
        m_count_nodes;
    T m_line;
    node<T> *m_head;
public:
    list(void);
    ~list(void);
    void add_end(T);
    void print();
};
#endif

  • Ich denke nicht, dass dies eine großartige Idee ist. Das Einschließen von .cpp-Dateien sendet die falsche Nachricht. Wenn Sie beabsichtigen, dass der Benutzer beide Dateien einbezieht. nennen Sie sie code.h und code_impl.h oder ähnlich.

    – Mark McKenna

    8. März 2013 um 15:21 Uhr

  • Ich stimme zu. Es gibt kaum einen Grund, jemals eine .cpp-Datei in Ihre Quelle aufzunehmen, und abhängig von Ihren Projekteinstellungen kann dies dem Compiler sogar zusätzliche Kopfschmerzen bereiten

    – Rechteck ist gleich

    12. März 2013 um 6:31 Uhr

994370cookie-checkWarum erhalte ich bei der Verwendung von Vorlagen die Fehlermeldung „Ungelöstes externes Symbol“? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy