it-swarm.com.de

g ++ undefined Verweis auf typeinfo

Ich bin gerade auf den folgenden Fehler gestoßen (und habe die Lösung online gefunden, aber im Stack Overflow nicht vorhanden):

(.gnu.linkonce. [stuff]): undefined Verweis auf [Methode] [Objekt file] :(. gnu.linkonce. [stuff]): undefinierter Verweis auf `typeinfo für [Klassenname]'

Warum könnte man einen dieser "undefinierten Verweise auf typeinfo" Linker-Fehler bekommen?

(Bonuspunkte, wenn Sie erklären können, was sich hinter den Kulissen abspielt.)

177
cdleary

Ein möglicher Grund ist, dass Sie eine virtuelle Funktion deklarieren, ohne sie zu definieren.

Wenn Sie es deklarieren, ohne es in derselben Kompilierungseinheit zu definieren, geben Sie an, dass es an einer anderen Stelle definiert ist. Dies bedeutet, dass die Linkerphase versucht, sie in einer der anderen Kompilierungseinheiten (oder Bibliotheken) zu finden.

Ein Beispiel für die Definition der virtuellen Funktion ist:

virtual void fn() { /* insert code here */ }

In diesem Fall hängen Sie der Deklaration eine Definition an, was bedeutet, dass der Linker sie später nicht auflösen muss.

Die Linie

virtual void fn();

deklariert fn(), ohne es zu definieren, und verursacht die Fehlermeldung, nach der Sie gefragt wurden.

Es ist dem Code sehr ähnlich:

extern int i;
int *pi = &i;

was besagt, dass die ganze Zahl i in einer anderen Kompilierungseinheit deklariert ist, die zum Zeitpunkt der Verknüpfung aufgelöst werden muss (andernfalls kann pi nicht auf ihre Adresse gesetzt werden).

186
paxdiablo

Dies kann auch passieren, wenn Sie -fno-rtti- und -frtti-Code mischen. Dann müssen Sie sicherstellen, dass für jede Klasse, auf die type_info im Code -frtti zugegriffen wird, die Schlüsselmethode mit -frtti kompiliert wird. Ein solcher Zugriff kann auftreten, wenn Sie ein Objekt der Klasse erstellen, dynamic_cast usw. verwenden.

[ Quelle ]

126

Dies tritt auf, wenn deklarierte (nicht reine) virtuelle Funktionen fehlende Körper sind. In Ihrer Klassendefinition etwa so:

virtual void foo();

Sollte definiert werden (Inline oder in einer verknüpften Quelldatei):

virtual void foo() {}

Oder als rein virtuell deklariert:

virtual void foo() = 0;
39
cdleary

Zitat aus dem gcc-Handbuch :

Bei polymorphen Klassen (Klassen mit virtuellen Funktionen) wird das type_info-Objekt zusammen mit der vtable ausgeschrieben. [...] Bei allen anderen Typen schreiben wir das type_info-Objekt, wenn es verwendet wird: Beim Anwenden von `typeid 'auf einen Ausdruck ein Objekt werfen oder auf einen Typ in einer catch-Klausel oder einer Ausnahmespezifikation verweisen.

Und etwas früher auf derselben Seite:

Wenn die Klasse nicht virtuelle Inline-Funktionen deklariert, wird die erste als Schlüsselmethode für die Klasse ausgewählt, und die vtable wird nur in der Übersetzungseinheit ausgegeben, in der die Schlüsselmethode definiert ist.

Dieser Fehler tritt also auf, wenn bei der Schlüsselmethode die Definition fehlt, wie in den anderen Antworten bereits erwähnt wurde.

25
CesarB

Wenn Sie eine .so-Datei mit einer anderen verknüpfen, ist eine weitere Möglichkeit das Kompilieren mit "-fvisibility = hidden" in gcc oder g ++. Wenn beide .so-Dateien mit "-fvisibility = hidden" erstellt wurden und die Schlüsselmethode sich nicht in derselben .so-Implementierung wie die Implementierung einer virtuellen Funktion befindet, wird die vtable oder die typeinfo der ersten nicht angezeigt. Für den Linker sieht dies wie eine unimplementierte virtuelle Funktion aus (wie in den Antworten von paxdiablo und cdleary).

In diesem Fall müssen Sie eine Ausnahme für die Sichtbarkeit der Basisklasse mit machen 

__attribute__ ((visibility("default")))

in der Klassendeklaration. Zum Beispiel,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

Eine andere Lösung ist natürlich, "-fvisibility = hidden" nicht zu verwenden. Dies macht den Compiler und den Linker kompliziert, möglicherweise zu Lasten der Codeleistung.

18
human

Die vorherigen Antworten sind richtig, aber dieser Fehler kann auch dadurch verursacht werden, dass versucht wird, typeid für ein Objekt einer Klasse zu verwenden, die keine virtuelle Funktionen hat. C++ RTTI erfordert eine vtable, daher benötigen Klassen, für die Sie die Typidentifizierung durchführen möchten, mindestens eine virtuelle Funktion. 

Wenn Sie möchten, dass die Typinformationen für eine Klasse funktionieren, für die Sie eigentlich keine virtuellen Funktionen möchten, müssen Sie den Destruktor als virtuell definieren.

13
Tyler McHenry

Mögliche Lösungen für Code, der sich mit RTTI- und Nicht-RTTI-Bibliotheken befasst:

a) Rekompilieren Sie alles mit -frtti oder -fno-rtti 
b) Wenn a) für Sie nicht möglich ist, versuchen Sie Folgendes:

Angenommen, libfoo wird ohne RTTI erstellt. Ihr Code verwendet libfoo und wird mit RTTI kompiliert. Wenn Sie in libfoo eine Klasse (Foo) mit virtuellen Klassen verwenden, tritt wahrscheinlich ein Link-Time-Fehler auf, der besagt, dass Typeinfo für die Klasse Foo fehlt.

Definieren Sie eine andere Klasse (z. B. FooAdapter), die keine virtuelle Klasse hat und Aufrufe an Foo weiterleitet, die Sie verwenden.

Kompilieren Sie FooAdapter in einer kleinen statischen Bibliothek, die RTTI nicht verwendet und nur von libfoo-Symbolen abhängig ist. Geben Sie einen Header an und verwenden Sie diesen stattdessen in Ihrem Code (der RTTI verwendet). Da FooAdapter über keine virtuelle Funktion verfügt, gibt es keine Typinformationen und Sie können Ihre Binärdatei verknüpfen. Wenn Sie viele verschiedene Klassen von libfoo verwenden, ist diese Lösung möglicherweise nicht bequem, aber es ist ein Anfang.

9
Francois

Ich habe nur ein paar Stunden mit diesem Fehler verbracht, und obwohl die anderen Antworten mir dabei geholfen haben zu verstehen, was los ist, haben sie mein spezielles Problem nicht behoben. 

Ich arbeite an einem Projekt, das mit clang++ und g++ kompiliert wird. Ich hatte keine Verbindungsprobleme mit clang++, bekam aber den undefined reference to 'typeinfo for-Fehler mit g++.

Der wichtige Punkt: Reihenfolge MATTERS mit g++ verknüpfen. Wenn Sie die Bibliotheken auflisten, die Sie in einer falschen Reihenfolge verknüpfen möchten, können Sie den Fehler typeinfo erhalten.

Siehe diese SO - Frage für weitere Details zum Verknüpfen der Reihenfolge mit gcc/g++.

8
dinkelk

Ähnlich wie bei der RTTI-, NO-RTTI-Diskussion oben, kann dieses Problem auch auftreten, wenn Sie dynamic_cast verwenden und den Objektcode nicht enthalten, der die Klassenimplementierung enthält.

Ich bin auf dieses Problem gestoßen, habe auf Cygwin aufgebaut und dann den Code nach Linux portiert. Die Make-Dateien, die Verzeichnisstruktur und sogar die gcc-Versionen (4.8.2) waren in beiden Fällen identisch, aber der Code, der unter Cygwin verlinkt wurde und ordnungsgemäß ausgeführt wurde, konnte unter Linux nicht gelinkt werden. Red Hat Cygwin hat anscheinend Compiler/Linker-Modifikationen vorgenommen, um die Anforderung an den Objektcode zu vermeiden.

Die Linux-Linker-Fehlernachricht hat mich richtig an die dynamic_cast-Zeile weitergeleitet, aber in früheren Nachrichten in diesem Forum musste ich eher nach fehlenden Funktionsimplementierungen als nach dem eigentlichen Problem suchen: fehlendem Objektcode. Mein Workaround bestand darin, eine virtuelle Typfunktion in der Basisklasse und der abgeleiteten Klasse zu ersetzen, z. virtual int isSpecialType (), anstatt dynamic_cast zu verwenden. Diese Technik vermeidet die Notwendigkeit, Objektimplementierungscode zu verknüpfen, damit dynamic_cast ordnungsgemäß funktioniert.

6
FNE

In der Basisklasse (einer abstrakten Basisklasse) deklarieren Sie einen virtuellen Destruktor. Da Sie einen Destruktor nicht als reine virtuelle Funktion deklarieren können, müssen Sie ihn entweder hier in der abstrakten Klasse definieren, nur eine Dummy-Definition wie virtuelle ~ base ( ) {} oder in einer der abgeleiteten Klassen.

Wenn Sie dies nicht tun, werden Sie zur Verbindungszeit in einem "undefinierten Symbol" landen. Da VMT einen Eintrag für alle reinen virtuellen Funktionen mit einer übereinstimmenden NULL hat, wird die Tabelle je nach Implementierung im aktualisiert abgeleitete Klasse. Für die nicht reinen, aber virtuellen Funktionen ist die Definition zum Zeitpunkt der Verknüpfung erforderlich, damit die VMT-Tabelle aktualisiert werden kann.

Verwenden Sie c ++ filt, um das Symbol zu entwirren. Wie $ c ++ filt _ZTIN10storageapi8BaseHostE Wird etwas wie "typeinfo for storageapi :: BaseHost" ausgegeben.

5
Prashanth

Ich habe gerade viele dieser Fehler bekommen. Was passiert ist, ist, dass ich eine nur-Header-Klasse in eine Header-Datei und eine CPP-Datei aufgeteilt habe. Ich habe mein Build-System jedoch nicht aktualisiert, sodass die CPP-Datei nicht kompiliert wurde. Ich hatte einfach undefinierte Verweise auf die Funktionen, die im Header deklariert, aber nicht implementiert wurden. 

Die Lösung bestand darin, das Buildsystem erneut auszuführen, um die neue cpp-Datei zu kompilieren und zu verknüpfen. 

3
Claudiu

in meinem Fall habe ich eine Drittanbieter-Bibliothek mit Header-Dateien und so-Dateien verwendet. Ich habe eine Klasse untergeordnet, und ein Link-Fehler wie dieser ist aufgetreten, als ich versuche, meine Unterklasse zu instanziieren.

wie von @sergiy erwähnt, wusste ich, dass es sich um das Problem von 'rtti' handeln kann. Ich konnte das Problem umgehen, indem Sie die Konstruktorimplementierung in eine separate .cpp-Datei packen und '-fno-rtti' -Kompilierungsflags auf die Datei anwenden. es läuft gut.

da mir der interne Fehler dieser Verbindung immer noch nicht ganz klar ist, bin ich nicht sicher, ob meine Lösung allgemein ist. Ich denke jedoch, dass es einen Versuch wert ist, bevor Sie die von @francois erwähnte Adaptermethode ausprobieren. und natürlich, wenn alle Quellcodes verfügbar sind (nicht in meinem Fall), rekompilieren Sie besser mit '-frtti' wenn möglich.

wenn Sie sich für eine Lösung entscheiden, versuchen Sie, die separate Datei so einfach wie möglich zu gestalten, und verwenden Sie keine ausgefallenen Funktionen von C++. Achten Sie besonders auf Boost-bezogene Dinge, da ein großer Teil davon von Rtti abhängt.

2
uwydoc

Ich habe den gleichen Fehler erhalten, als meine Schnittstelle (mit allen reinen virtuellen Funktionen) eine weitere Funktion benötigte und ich vergaß, sie "null" zu machen.

Ich hatte

class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };

Last vaClose ist nicht virtuell, so dass die kompilierten Benutzer nicht wissen, wo sie die Implementierung erhalten sollen, und wurden dadurch verwirrt. Meine Nachricht war:

... TCPClient.o :(. Rodata + 0x38): undefinierter Verweis auf `typeinfo für ICommProvider '

Einfache Änderung von

virtual int vaClose();

zu

virtual int vaClose() = 0;

das Problem behoben ich hoffe es hilft

1
Alex Paniutin

Ich stoße auf eine Situation, die selten ist, aber dies kann anderen Freunden in einer ähnlichen Situation helfen. Ich muss mit gcc 4.4.7 an einem älteren System arbeiten. Ich muss Code mit Unterstützung für C++ 11 oder höher kompilieren, daher baue ich die neueste Version von gcc 5.3.0. Beim Erstellen meines Codes und beim Verknüpfen mit den Abhängigkeiten, wenn die Abhängigkeit mit einem älteren Compiler erstellt wird, wurde der Fehler "undefinierter Verweis auf" angezeigt, obwohl ich den Verknüpfungspfad eindeutig mit -L/Pfad/zu/lib -llibname definiert habe. Einige Pakete wie boost und Projekte, die mit cmake erstellt werden, neigen dazu, den älteren Compiler zu verwenden, und sie verursachen normalerweise solche Probleme. Sie müssen einen langen Weg gehen, um sicherzustellen, dass sie den neueren Compiler verwenden. 

1
Kemin Zhou

In meinem Fall handelt es sich um ein reines Bibliotheksabhängigkeitsproblem, selbst wenn ich einen dynamic_cast-Aufruf habe. Nachdem das Makefile genügend Abhängigkeiten hinzugefügt hatte, war dieses Problem behoben.

0
Charlie

Stellen Sie sicher, dass Ihre Abhängigkeiten ohne -f-nortti erstellt wurden.

Für einige Projekte müssen Sie dies explizit festlegen, wie in RocksDB:

USE_RTTI=1 make shared_lib -j4
0
Vitaly Isaev