g ++ undefinierter Verweis auf typeinfo

208

Ich bin gerade auf den folgenden Fehler gestoßen (und habe die Lösung online gefunden, aber sie ist im Stapelüberlauf nicht vorhanden):

(.gnu.linkonce. [stuff]): undefinierter Verweis auf [method] [Objektdatei] :(. gnu.linkonce. [stuff]): undefinierter Verweis auf typeinfo für [Klassenname]

Warum könnte man einen dieser "undefinierten Verweise auf typeinfo" Linkerfehler bekommen?

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

cdleary
quelle
31
Ich weiß, dass es ein alter Beitrag ist, aber ich hatte heute das gleiche Problem, und die Lösung bestand einfach darin, meine virtuelle Funktion als virtuelles abc () {} in der Basisklasse anstelle von virtuellem abc () zu definieren. was den Fehler gab.
Nav
15
besser noch als virtual void abc() =0;(wenn die Basisversion nie aufgerufen wird)
dhardy
3
@Nav: Wenn Sie so definieren abc(), können Sie leicht vergessen, abc()die abgeleitete Klasse neu zu definieren und denken, dass alles in Ordnung ist, da Sie die Funktion weiterhin problemlos aufrufen können. Eine gute Vorgehensweise zum Implementieren von reinen virtuellen Funktionen finden Sie in diesem Artikel . Hiermit wird die Funktion "Reine virtuelle Funktion aufgerufen" gedruckt und anschließend das Programm zum Absturz gebracht.
HelloGoodbye
1
Ich hatte den gleichen Fehler. Ich habe festgestellt, dass das Ändern der Reihenfolge der Verweise auf "lib" hilfreich sein kann. Ich habe gerade Problembibliotheken vom Anfang an das Ende der Liste
verschoben
2
GAH. Dies ist jetzt mindestens das zweite Mal, dass ich genau zu dieser Seite navigiert bin, um den Kommentar von @dhardy zu lesen und mir 'Doh' zu sagen. Ich habe gerade 45 Minuten damit verbracht, ein verrücktes Verhalten aufzuspüren, und alles, was ich brauchte, war = 0;.
Dwanderson

Antworten:

222

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, es 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 fügen Sie der Deklaration eine Definition hinzu, 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 haben.

Es ist dem Code sehr ähnlich:

extern int i;
int *pi = &i;

Dies besagt, dass die Ganzzahl iin einer anderen Kompilierungseinheit deklariert ist, die zur Verbindungszeit aufgelöst werden muss (andernfalls pikann sie nicht auf ihre Adresse gesetzt werden).

paxdiablo
quelle
28
Es ist falsch zu sagen, dass dies virtual void fn() = 0eine Definition ist. Es ist keine Definition, sondern eine bloße Erklärung . Der einzige Grund, warum der Linker nicht versucht, das Problem zu beheben, besteht darin, dass der entsprechende VMT-Eintrag nicht auf einen Funktionskörper verweist (höchstwahrscheinlich einen Nullzeiger enthält). Niemand verbietet Ihnen jedoch, diese reine virtuelle Funktion nicht virtuell aufzurufen, dh unter Verwendung eines vollständig qualifizierten Namens. In diesem Fall wird der Linker wird für den Körper aussieht, und Sie werden die Funktion definieren. Und ja, Sie können einen Körper für eine reine virtuelle Funktion definieren.
AnT
1
Und manchmal muss man sogar einen Körper für eine reine virtuelle Funktion deklarieren.
Markieren Sie den
3
Der Compiler (g ++) teilt Ihnen mit, welches Symbol fehlt. Hinweis: Im Falle einer dynamischen Bibliotheksverknüpfung erhalten Sie möglicherweise einen verstümmelten Namen. Verwenden Sie c ++ filt <mangledNameVariable>, um es in eine lesbare Form zu bringen. Der typeinfo-Fehler mit einem Klassennamen war in meinem Fall auf eine fehlende Implementierung eines virtuellen Destruktors in einer Basisklasse zurückzuführen.
Chmike
1
In der Frage wird ausdrücklich erwähnt, dass typeinfo fehlt, was mit rtti zu tun hat. Siehe Kommentar von Damon in stackoverflow.com/questions/11904519/…
wilsonmichaelpatrick
1
@ gbmhunter, fair genug. Die Änderung vorgenommen.
Paxdiablo
149

Dies kann auch passieren, wenn Sie mischen -fno-rttiund -frtticodieren. Dann müssen Sie sicherstellen, dass für jede Klasse, auf die type_infoim -frttiCode zugegriffen wird , ihre Schlüsselmethode kompiliert wird -frtti. Ein solcher Zugriff kann erfolgen, wenn Sie ein Objekt der Klasse erstellen, verwenden dynamic_castusw.

[ Quelle ]

Sergiy Belozorov
quelle
20
ICH DANKE DIR SEHR. Das hat mein Problem nach 5 Stunden Suche behoben.
Steipete
1
Quelllink ist tot, es war sicherlich das gleiche wie permalink.gmane.org/gmane.comp.gcc.help/32475
Mathe
1
Vielen Dank für den Hinweis. Die Originalseite ist hier noch verfügbar: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov
3
StackOverflow.com wieder zur Rettung! Ich wünschte, ich könnte mehr als einmal abstimmen. Nachdem ich eine Stunde lang mit dem Kopf auf die Tastatur geschlagen hatte, war Ihre Antwort genau das, was ich brauchte.
Spartygw
1
n + 1 Leben gerettet und zählt immer noch :)
Gabriel
53

Dies tritt auf, wenn deklarierten (nicht reinen) virtuellen Funktionen Körper fehlen. In Ihrer Klassendefinition so etwas wie:

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;
cdleary
quelle
27

Zitat aus dem gcc-Handbuch :

Für polymorphe Klassen (Klassen mit virtuellen Funktionen) wird das type_info-Objekt zusammen mit der vtable [...] ausgeschrieben. Für alle anderen Typen schreiben wir das type_info-Objekt aus, wenn es verwendet wird: wenn `typeid 'auf einen Ausdruck angewendet wird, Auslösen eines Objekts oder Verweisen auf einen Typ in einer catch-Klausel oder Ausnahmespezifikation.

Und etwas früher auf derselben Seite:

Wenn die Klasse nicht-inline, nicht reine virtuelle 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 der "Schlüsselmethode" die Definition fehlt, wie andere bereits erwähnte Antworten zeigen.

CesarB
quelle
2
In meinem Fall hatte ich eine Basisklasse, die virtuelle Methoden deklarierte, aber nicht definierte, die nicht rein virtuell waren. Nachdem ich sie rein virtuell gemacht hatte, was ich damit meinte, verschwanden die Linker-Fehler.
Tatiana Racheva
@TatianaRacheva Danke! Die Fehlerberichterstattung vom Linker ist weniger als hilfreich und für eine große Schnittstelle ist es sehr leicht, das Fehlen von '= 0;' zu übersehen. für reine virtuelle!
Rholmes
20

Wenn Sie eine .so mit einer anderen verknüpfen, besteht eine weitere Möglichkeit darin, mit "-fvisibility = hidden" in gcc oder g ++ zu kompilieren. Wenn beide .so-Dateien mit "-fvisibility = hidden" erstellt wurden und sich die Schlüsselmethode nicht in derselben .so-Datei wie eine andere Implementierung der virtuellen Funktion befindet, wird in letzterer die vtable oder typeinfo der ersteren nicht angezeigt. Für den Linker sieht dies wie eine nicht implementierte 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 besteht natürlich darin, "-fvisibility = hidden" nicht zu verwenden. Dies erschwert den Compiler und den Linker, was sich möglicherweise nachteilig auf die Codeleistung auswirkt.

Mensch
quelle
1
Sie müssen die Basisklasse nicht exportieren (einblenden), wenn sie abstrakt oder nicht verwendet wird, sondern nur die nicht virtuellen Funktionen, normalerweise nur den Konstruktor. Die abgeleiteten Klassen müssen dagegen exportiert werden, wenn sie verwendet werden.
Chris Huang-Leaver
fühlt sich an wie ein Hack, aber es hat die Symptome auf meiner Seite gelöst. Vielen Dank !
Malat
15

Die vorherigen Antworten sind korrekt, aber dieser Fehler kann auch durch den Versuch verursacht werden, typeid für ein Objekt einer Klasse zu verwenden, das keine virtuellen Funktionen hat. Für C ++ RTTI ist eine vtable erforderlich, sodass für Klassen, für die Sie eine Typidentifizierung durchführen möchten, mindestens eine virtuelle Funktion erforderlich ist.

Wenn Sie möchten, dass Typinformationen für eine Klasse funktionieren, für die Sie keine virtuellen Funktionen benötigen, machen Sie den Destruktor virtuell.

Tyler McHenry
quelle
2
Upmodded, weil ich denke, dass dies eher die Ursache für diese spezifische Fehlermeldung ist (im Gegensatz zum allgemeineren Fall undefinierter Methoden ...)
Alastair
4
Eine Sache, an die ich mich mit SO gewöhnen musste, ist, dass ich mich nicht auf "obige" Antworten beziehe, da sich die Reihenfolge aufgrund von Stimmen ändern kann. Normalerweise beziehe ich mich jetzt nicht auf andere Antworten, da diese auch gelöscht werden können. Mein Glaube ist, dass Antworten eigenständig sein sollten. Ich beziehe mich jedoch immer noch auf Benutzernamen für die Zuordnung.
Paxdiablo
Sie können typeid ohne vtable verwenden. Siehe meine Antwort für die Zitate aus dem gcc-Handbuch.
CesarB
11

Ich habe nur ein paar Stunden mit diesem Fehler verbracht, und während die anderen Antworten hier mir geholfen haben zu verstehen, was los war, haben sie mein spezielles Problem nicht behoben.

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

Der wichtige Punkt: Reihenfolge verknüpfen mit g++. Wenn Sie die Bibliotheken, die Sie verknüpfen möchten, in einer falschen Reihenfolge auflisten, wird der typeinfoFehler angezeigt.

In dieser SO-Frage finden Sie weitere Informationen zum Verknüpfen der Bestellung mit gcc/ g++.

dinkelk
quelle
Danke dir!!! Ich habe über einen Tag lang versucht herauszufinden, warum ich diesen Fehler erhalten habe und nichts hat funktioniert, bis ich diese Antwort und die Antwort gesehen habe, mit der Sie verlinkt haben. Vielen Dank!!
Irene
10

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

a) Kompilieren Sie alles mit -frtti oder -fno-rtti neu.
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 Virtuals verwenden, tritt wahrscheinlich ein Link-Time-Fehler auf, der besagt: Typeinfo für Klasse Foo fehlt.

Definieren Sie eine andere Klasse (z. B. FooAdapter), die keine virtuelle Klasse hat und von Ihnen verwendete Anrufe an Foo weiterleitet.

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

Francois
quelle
Dies war es für mich, eine Verknüpfung zu einer Bibliothek mit verschiedenen RTTI-Einstellungen herzustellen.
Sumpf
6

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

Ich bin auf dieses Problem gestoßen, als ich auf Cygwin aufgebaut und dann Code nach Linux portiert habe. Die make-Dateien, die Verzeichnisstruktur und sogar die gcc-Versionen (4.8.2) waren in beiden Fällen identisch, aber der Code wurde unter Cygwin verknüpft und funktionierte korrekt, konnte jedoch unter Linux nicht verknüpft werden. Red Hat Cygwin hat anscheinend Compiler / Linker-Änderungen vorgenommen, die die Anforderung der Objektcode-Verknüpfung vermeiden.

Die Linux-Linker-Fehlermeldung hat mich ordnungsgemäß zur Zeile dynamic_cast weitergeleitet, aber in früheren Nachrichten in diesem Forum habe ich eher nach fehlenden Funktionsimplementierungen als nach dem eigentlichen Problem gesucht: fehlender Objektcode. Meine Problemumgehung bestand darin, eine virtuelle Typfunktion in der Basisklasse und der abgeleiteten Klasse zu ersetzen, z. B. virtual int isSpecialType (), anstatt dynamic_cast zu verwenden. Diese Technik vermeidet die Notwendigkeit, Objektimplementierungscode zu verknüpfen, damit dynamic_cast ordnungsgemäß funktioniert.

FNE
quelle
5

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 auch hier in der abstrakten Klasse definieren, nur eine Dummy-Definition wie virtual ~ base ( ) {} reicht aus oder in einer der abgeleiteten Klassen.

Wenn Sie dies nicht tun, werden Sie zum Zeitpunkt der Verknüpfung in einem "undefinierten Symbol" angezeigt. Da VMT einen Eintrag für alle rein virtuellen Funktionen mit einem übereinstimmenden NULL hat, wird die Tabelle abhängig von der Implementierung in der abgeleiteten Klasse aktualisiert. Für die nicht reinen, aber virtuellen Funktionen wird die Definition zum Zeitpunkt der Verknüpfung benötigt, damit die VMT-Tabelle aktualisiert werden kann.

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

Prashanth
quelle
3

Ich habe gerade viele dieser Fehler bekommen. Was passiert ist, dass ich eine Nur-Header-Datei-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. Unter einfach undefinierten Verweisen auf die Funktionen, die im Header deklariert, aber nicht implementiert wurden, habe ich viele dieser typeinfo-Fehler erhalten.

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

Claudiu
quelle
3

In meinem Fall habe ich eine Drittanbieter-Bibliothek mit Header-Dateien und so weiter verwendet. Ich habe eine Klasse unterklassifiziert, und ein Linkfehler wie dieser ist aufgetreten, als ich versucht habe, meine Unterklasse zu instanziieren.

Wie von @sergiy erwähnt, da ich wusste, dass es das Problem von 'rtti' sein könnte, gelang es mir, es zu umgehen, indem ich die Konstruktorimplementierung in eine separate CPP-Datei legte und '-fno-rtti'-Kompilierungsflags auf die Datei anwendete . Es funktioniert gut.

Da mir der interne Fehler dieses Links immer noch nicht ganz klar ist, bin ich mir nicht sicher, ob meine Lösung allgemein ist. Ich denke jedoch, dass es einen Versuch wert ist, bevor Sie den Adapter wie von @francois erwähnt ausprobieren. und wenn alle Quellcodes verfügbar sind (in meinem Fall nicht), kompilieren Sie sie nach Möglichkeit besser mit '-frtti'.

Wenn Sie meine Lösung ausprobieren möchten, 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ßteil davon von RTI abhängt.

uwydoc
quelle
2

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

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, daher wusste die Kompilierung nicht, wo sie implementiert werden sollte, und war dadurch verwirrt. Meine Nachricht war:

... TCPClient.o :(. Rodata + 0x38): undefinierter Verweis auf "typeinfo for ICommProvider"

Einfacher Wechsel von

virtual int vaClose();

zu

virtual int vaClose() = 0;

Das Problem wurde behoben. ich hoffe es hilft

Alex Paniutin
quelle
1

Ich stoße auf eine Situation, die selten ist, aber dies kann anderen Freunden in einer ähnlichen Situation helfen. Ich muss auf einem älteren System mit gcc 4.4.7 arbeiten. Ich muss Code mit Unterstützung für C ++ 11 oder höher kompilieren, damit ich die neueste Version von gcc 5.3.0 erstelle. Beim Erstellen meines Codes und beim Verknüpfen mit den Abhängigkeiten, wenn die Abhängigkeit mit einem älteren Compiler erstellt wurde, wurde der Fehler "undefinierter Verweis auf" angezeigt, obwohl ich den Verknüpfungspfad mit -L / path / to / lib -llibname klar definiert habe. Einige Pakete wie Boost und Projekte, die mit cmake erstellt wurden, tendieren normalerweise 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.

Kemin Zhou
quelle
1

In meinem Fall handelt es sich lediglich um ein Problem mit der Bibliotheksabhängigkeit, selbst wenn ich einen Aufruf von dynamic_cast habe. Nachdem dem Makefile genügend Abhängigkeiten hinzugefügt wurden, war dieses Problem behoben.

Charlie
quelle
0

Überprüfen Sie, ob Ihre Abhängigkeiten ohne kompiliert wurden -f-nortti.

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

USE_RTTI=1 make shared_lib -j4
Vitaly Isaev
quelle
0

In meinem Fall war es eine virtuelle Funktion in einer Schnittstellenklasse, die nicht als rein virtuell definiert wurde.

class IInterface
{
public:
  virtual void Foo() = 0;
}

Ich habe das = 0bisschen vergessen .

Gänsehaut
quelle