web-dev-qa-db-fra.com

g ++ référence indéfinie à typeinfo

J'ai juste rencontré l'erreur suivante (et trouvé la solution en ligne, mais elle n'est pas présente dans Stack Overflow):

(.gnu.linkonce. [stuff]): non défini référence à [méthode] [objet fichier] :(. gnu.linkonce. [stuff]): référence non définie à `typeinfo pour [nom du cours]'

Pourquoi peut-on obtenir l'une de ces erreurs "Undefined reference to typeinfo"?

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

177
cdleary

Une des raisons possibles 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 tentera de le trouver dans l'une des autres unités de compilation (ou bibliothèques).

Voici un exemple de définition de la fonction virtuelle:

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

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

La ligne

virtual void fn();

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

C'est très similaire au code:

extern int i;
int *pi = &i;

qui indique que l'entier i est déclaré dans une autre unité de compilation qui doit être résolue au moment de la liaison (sinon, pi ne peut pas être défini sur son adresse).

186
paxdiablo

Cela peut également se produire lorsque vous mélangez les codes -fno-rtti et -frtti. Ensuite, vous devez vous assurer que la méthode de clé de chaque classe à laquelle type_info est accédé dans le code -frtti est compilée avec -frtti. Cet accès peut se produire lorsque vous créez un objet de la classe, utilisez dynamic_cast etc.

[ la source ]

126
Sergiy Belozorov

Cela se produit lorsque des fonctions virtuelles déclarées (non pures) sont des corps manquants. 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é pur virtuel:

virtual void foo() = 0;
39
cdleary

Citant de le manuel de gcc }:

Pour les classes polymorphes (classes avec fonctions virtuelles), l'objet type_info est écrit avec la vtable [...] Pour tous les autres types, nous écrivons l'objet type_info lorsqu'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, non-pures, la première est choisie comme «méthode de clé» pour la classe et la vtable est uniquement émise dans l'unité de traduction où la méthode de clé est définie.

Donc, cette erreur se produit lorsque la définition de la "méthode de clé" manque, de même que d'autres réponses déjà mentionnées.

25
CesarB

Si vous liez un fichier .so à un autre, une possibilité supplémentaire est de compiler avec "-fvisibility = hidden" dans gcc ou g ++. Si les deux fichiers .so ont été généré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 le typeinfo de la précédente. 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 créer 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();
}

Bien entendu, une autre solution consiste à ne pas utiliser "-fvisibility = hidden". Cela complique les choses pour le compilateur et l'éditeur de liens, éventuellement au détriment des performances du code.

18
human

Les réponses précédentes sont correctes, mais cette erreur peut également être provoquée en essayant d'utiliser typeid sur un objet d'une classe comportant no fonctions virtuelles. C++ RTTI requiert une vtable. Par conséquent, les classes sur lesquelles vous souhaitez effectuer l'identification du type nécessitent au moins une fonction virtuelle. 

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

13
Tyler McHenry

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

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

Supposons que libfoo soit construit sans RTTI. Votre code utilise libfoo et compile avec RTTI. Si vous utilisez une classe (Foo) dans libfoo qui contient des virtuels, vous risquez de rencontrer une erreur de temps de lien indiquant: typeinfo manquant pour la classe Foo.

Définissez une autre classe (par exemple, FooAdapter) qui n’a pas de virtualité 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-lui un en-tête et utilisez-le à la place dans votre code (qui utilise RTTI). Comme FooAdapter n'a pas de fonction virtuelle, il n'aura pas de typeinfo et vous pourrez relier 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.

9
Francois

Je viens de passer quelques heures sur cette erreur et, alors que 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 en utilisant clang++, mais je recevais l'erreur undefined reference to 'typeinfo for avec g++.

Le point important: Ordre de liaison QUESTIONS d'ordre avec g++. Si vous répertoriez les bibliothèques que vous souhaitez lier dans un ordre incorrect, vous pouvez obtenir l'erreur typeinfo.

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

8
dinkelk

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

J'ai rencontré ce problème en construisant sur Cygwin puis en transférant le code sous 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 sous Cygwin mais ne permettait pas la liaison sous Linux. Red Hat Cygwin a apparemment apporté des modifications au compilateur/éditeur de liens afin d'éviter l'exigence de liaison de code d'objet.

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

6
FNE

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 en tant que fonction virtuelle pure, vous devez soit le définir ici, dans la classe abstraite, juste une définition factice comme virtual ~ base ( ) {} fera l'affaire ou dans l'une des classes dérivées.

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

Utilisez c ++ filt pour démêler le symbole. Comme pour $ c ++, le fichier _ZTIN10storageapi8BaseHostE .__ produira quelque chose du type "typeinfo for storageapi :: BaseHost".

5
Prashanth

J'ai beaucoup de ces erreurs tout à l'heure. En réalité, j'ai scindé une classe contenant uniquement un en-tête en un fichier d'en-tête et un fichier cpp. Cependant, je n'ai pas mis à jour mon système de construction, le fichier cpp n'a donc pas été compilé. Parmi les références indé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 compilation pour compiler et lier le nouveau fichier cpp. 

3
Claudiu

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

comme mentionné par @sergiy, sachant qu'il pourrait s'agir du problème de 'rtti', j'ai réussi à la contourner par mettre l'implémentation du constructeur dans un fichier .cpp séparé et appliquer les indicateurs de compilation '-fno-rtti' au fichier . Ça marche bien.

comme je ne suis toujours pas tout à fait clair au sujet de 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 d'essayer 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++. porter une attention particulière aux choses liées au boost, car cela dépend en grande partie de rtti.

2
uwydoc

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

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

Last vaClose n'étant pas virtuel, la compilation ne savait pas où trouver l'implémentation et était donc confuse. mon message était:

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

Simple changement de

virtual int vaClose();

à

virtual int vaClose() = 0;

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

1
Alex Paniutin

Je rencontre une situation rare, mais cela peut aider d’autres amis dans une situation similaire. Je dois travailler sur un système plus ancien avec gcc 4.4.7. Je dois compiler le 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 créée avec un compilateur plus ancien, l'erreur "référence non définie à" s'est produite, même si j'ai clairement défini le chemin de liaison avec -L/path/to/lib -llibname. Certains paquets tels que boost et les projets construits avec cmake ont généralement tendance à utiliser le compilateur plus ancien et causent généralement de tels problèmes. Vous devez vous assurer que le nouveau compilateur est utilisé. 

1
Kemin Zhou

Dans mon cas, il s'agit purement d'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épendances dans le makefile, ce problème avait disparu.

0
Charlie

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
Vitaly Isaev