g ++ référence non définie à typeinfo


209

Je viens de rencontrer l'erreur suivante (et j'ai trouvé la solution en ligne, mais elle n'est pas présente dans Stack Overflow):

(.gnu.linkonce. [stuff]): référence non définie à [méthode] [fichier objet] :(. gnu.linkonce. [stuff]): référence non définie à `typeinfo for [classname] '

Pourquoi pourrait-on obtenir l'une de ces erreurs de l'éditeur de liens "référence non définie à typeinfo"?

(Points bonus si vous pouvez expliquer ce qui se passe dans les coulisses.)


31
Je sais que c'est un ancien article, mais j'ai eu le même problème aujourd'hui, et la solution était simplement de définir ma fonction virtuelle comme virtual abc () {} dans la classe de base, au lieu de virtual abc (); qui a donné l'erreur.
Nav

15
mieux encore virtual void abc() =0;(si la version de base n'est jamais appelée)
dhardy

3
@Nav: Si vous définissez abc()comme ça, vous pouvez facilement oublier de redéfinir abc()dans la classe dérivée et penser que tout va bien, car vous pourrez toujours appeler la fonction sans aucun problème. Une bonne pratique pour implémenter des fonctions virtuelles pures se trouve dans cet article , et cela consiste à faire en sorte que la fonction affiche "Fonction virtuelle pure appelée", puis plante le programme.
HelloGoodbye

1
j'avais la même erreur. j'ai trouvé que changer l'ordre des références à "lib" peut aider. je viens de déplacer le problème des bibliothèques du début à la fin de la liste et cela a résolu le problème
javapowered

2
GAH. C'est maintenant au moins la deuxième fois que je navigue exactement vers cette page, pour lire le commentaire de @dhardy et me dire 'Doh'. Je viens de passer 45 minutes à essayer de retrouver un comportement fou et tout ce dont j'avais besoin était = 0;.
dwanderson

Réponses:


223

Une raison possible est que vous déclarez une fonction virtuelle sans la définir.

Lorsque vous le déclarez sans le définir dans la même unité de compilation, vous indiquez qu'il est défini ailleurs - cela signifie que la phase de l'éditeur de liens essaiera de le trouver dans l'une des autres unités de compilation (ou bibliothèques).

Un exemple de définition de la fonction virtuelle est:

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

Dans ce cas, vous joignez une définition à la déclaration, ce qui signifie que l'éditeur de liens n'a pas besoin de la résoudre ultérieurement.

La ligne

virtual void fn();

déclare fn() sans le définir et provoquera le message d'erreur que vous avez demandé.

Il est très similaire au code:

extern int i;
int *pi = &i;

qui indique que l'entier iest déclaré dans une autre unité de compilation qui doit être résolue au moment de la liaison (sinon pine peut pas être définie sur son adresse).


28
Il est incorrect de dire que virtual void fn() = 0c'est une définition. Ce n'est pas une définition, mais une simple déclaration . La seule raison pour laquelle l'éditeur de liens n'essaie pas de le résoudre est que l'entrée VMT correspondante ne fera pas référence à un corps de fonction (contiendra probablement un pointeur nul). Cependant, personne ne vous interdit d'appeler cette fonction virtuelle pure de manière non virtuelle, c'est-à-dire en utilisant un nom complet. Dans ce cas , l'éditeur de liens se chercher le corps, et vous devrez définir la fonction. Et oui, vous pouvez définir un corps pour une fonction virtuelle pure.
2010

1
Et parfois, il faut même déclarer un corps pour une fonction virtuelle pure.
marquez

3
Le compilateur (g ++) vous dira quel est le symbole manquant. Remarque: En cas de liaison de bibliothèque dynamique, vous pouvez obtenir un nom modifié. Utilisez c ++ filt <mangledNameVariable> pour l'obtenir sous une forme lisible. L'erreur typeinfo avec un nom de classe était dans mon cas à cause d'une implémentation de destructeur virtuel manquante dans une classe de base.
chmike

1
La question mentionne spécifiquement que c'est typeinfo qui manque, qui a à voir avec rtti. Voir le commentaire de Damon dans stackoverflow.com/questions/11904519/…
wilsonmichaelpatrick

1
@gbmhunter, assez juste. J'ai fait le changement.
paxdiablo

150

Cela peut également se produire lorsque vous mixez -fno-rttiet -frtticodez. Ensuite, vous devez vous assurer que toute classe, qui type_infoest accessible dans le -frtticode, a sa méthode clé compilée avec -frtti. Un tel accès peut se produire lorsque vous créez un objet de la classe, utilisezdynamic_cast etc.

[ source ]


20
MERCI BEAUCOUP. Cela a résolu mon problème après 5 heures de recherche.
steipete

1
le lien source est mort, c'était sûrement le même que permalink.gmane.org/gmane.comp.gcc.help/32475
math

1
Merci de l'avoir signalé. La page d'origine est toujours disponible ici: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov

3
StackOverflow.com à nouveau à la rescousse! J'aimerais pouvoir voter plus d'une fois. Après m'être cogné la tête sur le clavier pendant une heure, votre réponse était ce dont j'avais besoin.
spartygw

1
n + 1 vies sauvées et comptent toujours :)
Gabriel

53

Cela se produit lorsque des fonctions virtuelles déclarées (non pures) manquent de corps. Dans votre définition de classe, quelque chose comme:

virtual void foo();

Doit être défini (en ligne ou dans un fichier source lié):

virtual void foo() {}

Ou déclaré virtuel pur:

virtual void foo() = 0;

27

Citant du manuel de gcc :

Pour les classes polymorphes (classes avec fonctions virtuelles), l'objet type_info est écrit avec le vtable [...] Pour tous les autres types, nous écrivons l'objet type_info quand il est utilisé: lors de l'application de `typeid 'à une expression, lancer un objet ou faire référence à un type dans une clause catch ou une spécification d'exception.

Et un peu plus tôt sur la même page:

Si la classe déclare des fonctions virtuelles non en ligne et non pures, la première est choisie comme «méthode clé» pour la classe, et la table virtuelle n'est émise que dans l'unité de traduction où la méthode clé est définie.

Ainsi, cette erreur se produit lorsque la "méthode clé" manque sa définition, comme d'autres réponses déjà mentionnées.


2
Dans mon cas, j'avais une classe de base qui déclarait mais ne définissait pas de méthodes virtuelles qui n'étaient pas du virtuel pur. Une fois que je les ai rendus virtuels purs, ce que je voulais dire, les erreurs de l'éditeur de liens ont disparu.
Tatiana Racheva

@TatianaRacheva Merci! Le rapport d'erreurs de l'éditeur de liens est loin d'être utile et pour une grande interface, il est très facile de passer à côté du manque de '= 0;' pour du virtuel pur!
rholmes

21

Si vous liez un .so à un autre, une autre possibilité est de compiler avec "-fvisibility = hidden" dans gcc ou g ++. Si les deux fichiers .so ont été créés avec "-fvisibility = hidden" et que la méthode key n'est pas dans le même .so qu'une autre implémentation de la fonction virtuelle, cette dernière ne verra pas la vtable ou typeinfo de la première. Pour l'éditeur de liens, cela ressemble à une fonction virtuelle non implémentée (comme dans les réponses de paxdiablo et cdleary).

Dans ce cas, vous devez faire une exception pour la visibilité de la classe de base avec

__attribute__ ((visibility("default")))

dans la déclaration de classe. Par exemple,

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

Une autre solution, bien sûr, consiste à ne pas utiliser "-fvisibility = hidden". Cela complique les choses pour le compilateur et l'éditeur de liens, peut-être au détriment des performances du code.


1
Vous n'avez pas besoin d'exporter (afficher) la classe de base si elle est abstraite ou inutilisée, juste les fonctions non virtuelles, normalement juste le constructeur. En revanche, les classes dérivées doivent être exportées si elles sont utilisées.
Chris Huang-Leaver

ressemble à un hack, mais cela a résolu les symptômes de mon côté. Merci !
malat

16

Les réponses précédentes sont correctes, mais cette erreur peut également être provoquée en tentant d'utiliser typeid sur un objet d'une classe qui n'a pas de fonctions virtuelles. C ++ RTTI nécessite une table virtuelle, donc les classes sur lesquelles vous souhaitez effectuer l'identification de type nécessitent au moins une fonction virtuelle.

Si vous souhaitez que les informations de type fonctionnent sur une classe pour laquelle vous ne voulez pas vraiment de fonctions virtuelles, rendez le destructeur virtuel.


2
Modifié parce que je pense que cela est plus susceptible d'être la cause de ce message d'erreur spécifique (par opposition au cas plus général des méthodes non définies ...)
Alastair

4
Une chose à laquelle je devais m'habituer avec SO ne fait pas référence aux réponses "ci-dessus" car l'ordre peut changer en fonction des votes. Je ne me réfère généralement pas à d'autres réponses maintenant, car elles peuvent également être supprimées. Ma conviction est que les réponses devraient être autonomes. Je me réfère toujours aux noms d'utilisateur pour l'attribution cependant.
paxdiablo

Vous pouvez utiliser typeid sans vtable; voir ma réponse pour les citations du manuel gcc.
CesarB

11

Je viens de passer quelques heures sur cette erreur, et même si les autres réponses ici m'ont aidé à comprendre ce qui se passait, elles n'ont pas résolu mon problème particulier.

Je travaille sur un projet qui compile en utilisant à la fois clang++et g++. Je n'avais aucun problème de liaison avec clang++, mais j'obtenais l' undefined reference to 'typeinfo forerreur avec g++.

Le point important: lier l'ordre MATTERS avec g++. Si vous répertoriez les bibliothèques que vous souhaitez lier dans un ordre incorrect, vous pouvez obtenir l' typeinfoerreur.

Voir cette question SO pour plus de détails sur la liaison de la commande avec gcc/ g++.


Je vous remercie!!! J'ai passé plus d'une journée à essayer de savoir pourquoi j'obtenais cette erreur et rien n'a fonctionné jusqu'à ce que je voie cette réponse et celle à laquelle vous êtes lié. Merci beaucoup!!
Irene

10

Solutions possibles pour le code traitant des bibliothèques RTTI et non RTTI:

a) Recompilez tout avec -frtti ou -fno-rtti
b) Si a) ne vous est pas possible, essayez ce qui suit:

Supposons que libfoo est construit sans RTTI. Votre code utilise libfoo et se compile avec RTTI. Si vous utilisez une classe (Foo) dans libfoo qui a des virtuels, vous risquez de rencontrer une erreur de liaison qui dit: typeinfo manquant pour la classe Foo.

Définissez une autre classe (par exemple, FooAdapter) qui n'a pas de virtuel et transférera les appels à Foo que vous utilisez.

Compilez FooAdapter dans une petite bibliothèque statique qui n'utilise pas RTTI et ne dépend que des symboles libfoo. Fournissez-en un en-tête et utilisez-le à la place dans votre code (qui utilise RTTI). Puisque FooAdapter n'a pas de fonction virtuelle, il n'aura pas de typeinfo et vous pourrez lier votre binaire. Si vous utilisez beaucoup de classes différentes de libfoo, cette solution peut ne pas être pratique, mais c'est un début.


C'était tout pour moi, un lien vers une bibliothèque avec différents paramètres RTTI.
marais

6

De manière similaire à la discussion RTTI, NO-RTTI ci-dessus, ce problème peut également se produire si vous utilisez dynamic_cast et ne parvenez pas à inclure le code objet contenant l'implémentation de classe.

J'ai rencontré ce problème en construisant sur Cygwin puis en portant du code sur Linux. Les fichiers make, la structure des répertoires et même les versions de gcc (4.8.2) étaient identiques dans les deux cas, mais le code était lié et fonctionnait correctement sur Cygwin mais n'a pas réussi à se lier sur Linux. Red Hat Cygwin a apparemment apporté des modifications au compilateur / éditeur de liens qui évitent l'exigence de liaison du code objet.

Le message d'erreur de l'éditeur de liens Linux m'a correctement dirigé vers la ligne dynamic_cast, mais les messages précédents de ce forum m'ont demandé de rechercher des implémentations de fonctions manquantes plutôt que le problème réel: code d'objet manquant. Ma solution de contournement consistait à remplacer une fonction de type virtuel dans la classe de base et dérivée, par exemple virtual int isSpecialType (), plutôt que d'utiliser dynamic_cast. Cette technique évite l'obligation de lier le code d'implémentation d'objet juste pour que dynamic_cast fonctionne correctement.


5

Dans la classe de base (une classe de base abstraite), vous déclarez un destructeur virtuel et comme vous ne pouvez pas déclarer un destructeur comme une fonction virtuelle pure, soit vous devez le définir ici dans la classe abstraite, juste une définition fictive comme virtual ~ base ( ) {} fera l'affaire, ou dans n'importe quelle classe dérivée.

Si vous ne le faites pas, vous vous retrouverez dans un "symbole non défini" au moment du lien. Puisque VMT a une entrée pour toutes les fonctions virtuelles pures avec un NULL correspondant car il met à jour la table en fonction de l'implémentation dans la classe dérivée. Mais pour les fonctions non pures mais virtuelles, il a besoin de la définition au moment de la liaison pour pouvoir mettre à jour la table VMT.

Utilisez c ++ filt pour démêler le symbole. Comme $ c ++ filt _ZTIN10storageapi8BaseHostE affichera quelque chose comme "typeinfo pour storageapi :: BaseHost".


3

J'ai eu beaucoup de ces erreurs tout à l'heure. Ce qui s'est passé, c'est que j'ai divisé une classe de fichiers d'en-tête uniquement en un fichier d'en-tête et un fichier cpp. Cependant, je n'ai pas mis à jour mon système de construction, donc le fichier cpp n'a pas été compilé. Parmi simplement avoir des références non définies aux fonctions déclarées dans l'en-tête mais non implémentées, j'ai eu beaucoup de ces erreurs typeinfo.

La solution consistait à réexécuter le système de génération pour compiler et lier le nouveau fichier cpp.


3

dans mon cas, j'ai utilisé une bibliothèque tierce avec des fichiers d'en-tête et donc un fichier. j'ai sous-classé une classe, et une erreur de lien comme celle-ci s'est produite lorsque j'essaie d'instancier ma sous-classe.

comme mentionné par @sergiy, sachant que cela pourrait être le problème de 'rtti', j'ai réussi à le contourner en plaçant l'implémentation du constructeur dans un fichier .cpp séparé et en appliquant des indicateurs de compilation '-fno-rtti' au fichier . Ça marche bien.

comme je ne suis pas encore tout à fait clair sur l'interne de cette erreur de lien, je ne sais pas si ma solution est générale. cependant, je pense que ça vaut le coup avant d'essayer l'adaptateur comme mentionné par @francois. et bien sûr, si tous les codes sources sont disponibles (pas dans mon cas), mieux vaut recompiler avec '-frtti' si possible.

une dernière chose, si vous choisissez d'essayer ma solution, essayez de rendre le fichier séparé aussi simple que possible et n'utilisez pas certaines fonctionnalités sophistiquées de C ++. prêter une attention particulière aux choses liées au boost, car cela dépend en grande partie de rtti.


2

J'ai la même erreur lorsque mon interface (avec toutes les fonctions virtuelles pures) avait besoin d'une fonction de plus et j'ai oublié de la "annuler".

j'ai eu

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(); };

Le dernier vaClose n'est pas virtuel, donc compilé ne savait pas où obtenir l'implémentation et s'est donc perdu. mon message était:

... TCPClient.o :(. Rodata + 0x38): référence non définie à `typeinfo for ICommProvider '

Changement simple de

virtual int vaClose();

à

virtual int vaClose() = 0;

résolu le problème. J'espère que ça aide


1

Je rencontre une situation rare, mais cela peut aider d'autres amis dans une situation similaire. Je dois travailler sur un ancien système avec gcc 4.4.7. Je dois compiler du code avec le support c ++ 11 ou supérieur, donc je construis la dernière version de gcc 5.3.0. Lors de la construction de mon code et de la liaison aux dépendances si la dépendance est construite avec un compilateur plus ancien, j'ai obtenu l'erreur `` référence non définie à '' même si j'ai clairement défini le chemin de liaison avec -L / chemin / vers / lib -llibname. Certains packages tels que boost et builds avec cmake ont généralement tendance à utiliser l'ancien compilateur, et ils provoquent généralement de tels problèmes. Vous devez faire un long chemin pour vous assurer qu'ils utilisent le nouveau compilateur.


1

Dans mon cas, c'est purement un problème de dépendance de bibliothèque même si j'ai un appel dynamic_cast. Après avoir ajouté suffisamment de dépendance dans le makefile, ce problème avait disparu.


0

Vérifiez que vos dépendances ont été compilées sans -f-nortti.

Pour certains projets, vous devez le définir explicitement, comme dans RocksDB:

USE_RTTI=1 make shared_lib -j4

0

Dans mon cas, c'était une fonction virtuelle dans une classe d'interface qui n'était pas définie comme un pur virtuel.

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

J'ai oublié le = 0bit.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.