g ++ riferimento indefinito a typeinfo


209

Ho appena riscontrato il seguente errore (e ho trovato la soluzione online, ma non è presente in Stack Overflow):

(.gnu.linkonce. [stuff]): riferimento indefinito a [method] [file oggetto] :(. gnu.linkonce. [stuff]): riferimento indefinito a `typeinfo per [classname] '

Perché si potrebbe ottenere uno di questi errori di linker "riferimento indefinito a typeinfo"?

(Punti bonus se puoi spiegare cosa sta succedendo dietro le quinte.)


31
So che è un vecchio post, ma oggi ho avuto lo stesso problema e la soluzione era semplicemente quella di definire la mia funzione virtuale come virtual abc () {} nella classe base, anziché virtual abc (); che ha dato l'errore.
Nav

15
meglio ancora come virtual void abc() =0;(se la versione di base non viene mai chiamata)
dhardy il

3
@Nav: se la definisci abc()così, puoi facilmente dimenticare di ridefinire abc()la classe derivata e pensare che tutto sia a posto, dal momento che potrai comunque chiamare la funzione senza alcun problema. In questo articolo si trova una buona pratica per l'implementazione di funzioni virtuali pure , che consiste nel fare in modo che la funzione stampi "Funzione virtuale pura chiamata" e quindi blocchi il programma.
Ciao Arrivederci,

1
stavo avendo lo stesso errore. ho scoperto che cambiare l'ordine dei riferimenti a "lib" può aiutare. ho appena spostato il problema lib dalla prima alla fine dell'elenco e questo ha risolto il problema
javapowered

2
GAH. Questa è ora almeno la seconda volta che sono passato esattamente a questa pagina, per leggere il commento di @dhardy e dire a me stesso "Doh". Ho appena trascorso 45 minuti cercando di rintracciare un comportamento folle e tutto ciò di cui avevo bisogno era = 0;.
Dwanderson,

Risposte:


223

Una possibile ragione è perché stai dichiarando una funzione virtuale senza definirla.

Quando lo dichiari senza definirlo nella stessa unità di compilazione, stai indicando che è definito da qualche altra parte - questo significa che la fase del linker proverà a trovarlo in una delle altre unità di compilazione (o librerie).

Un esempio di definizione della funzione virtuale è:

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

In questo caso, si allega una definizione alla dichiarazione, il che significa che il linker non deve risolverla in un secondo momento.

La linea

virtual void fn();

dichiara fn()senza definirlo e causerà il messaggio di errore richiesto.

È molto simile al codice:

extern int i;
int *pi = &i;

che indica che l'intero iè dichiarato in un'altra unità di compilazione che deve essere risolta al momento del collegamento (altrimenti pinon può essere impostato sul suo indirizzo).


28
Non è corretto affermare che si virtual void fn() = 0tratta di una definizione. Non è una definizione, ma una semplice dichiarazione . L'unico motivo per cui il linker non sta cercando di risolverlo è che la voce VMT corrispondente non farà riferimento a un corpo di funzione (conterrà probabilmente puntatore null). Tuttavia, nessuno ti proibisce di chiamare questa pura funzione virtuale in modo non virtuale, cioè utilizzando un nome completo. In questo caso il linker sarà cercare per il corpo, e si dovrà definire la funzione. E sì, puoi definire un corpo per una pura funzione virtuale.
AnT

1
E a volte si deve persino dichiarare un corpo per una pura funzione virtuale.
segna il

3
Il compilatore (g ++) ti dirà qual è il simbolo mancante. Nota: in caso di collegamento dinamico di librerie è possibile che venga visualizzato un nome alterato. Usa c ++ filt <mangledNameVariable> per ottenerlo in un formato leggibile. L'errore typeinfo con un nome di classe era nel mio caso a causa di un'implementazione del distruttore virtuale mancante in alcune classi di base.
chmike,

1
La domanda menziona specificamente che manca typeinfo, che ha a che fare con RTTI. Vedere commento da Damon in stackoverflow.com/questions/11904519/...
wilsonmichaelpatrick

1
@gbmhunter, abbastanza giusto. Fatto il cambiamento.
paxdiablo,

150

Questo può accadere anche quando mescoli -fno-rttie -frtticodifichi. Quindi è necessario assicurarsi che qualsiasi classe, a cui type_infosi accede nel -frtticodice, abbia compilato il proprio metodo chiave -frtti. Tale accesso può avvenire quando si crea un oggetto della classe, si usa dynamic_castecc.

[ fonte ]


20
GRAZIE MILLE. Ciò ha risolto il mio problema dopo 5 ore di ricerche.
steipete,

1
il link alla fonte è morto, era sicuramente lo stesso di permalink.gmane.org/gmane.comp.gcc.help/32475
matematica

1
Grazie per averlo segnalato. La pagina originale è ancora disponibile qui: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov

3
StackOverflow.com di nuovo in soccorso! Vorrei poter votare più di una volta. Dopo aver sbattuto la testa sulla tastiera per un'ora, la tua risposta era ciò di cui avevo bisogno.
spartygw,

1
n + 1 vite salvate e contano ancora :)
Gabriel

53

Ciò si verifica quando alle funzioni virtuali dichiarate (non pure) mancano i corpi. Nella definizione della tua classe, qualcosa del tipo:

virtual void foo();

Dovrebbe essere definito (in linea o in un file sorgente collegato):

virtual void foo() {}

O dichiarato puro virtuale:

virtual void foo() = 0;

27

Citando dal manuale di gcc :

Per le classi polimorfiche (classi con funzioni virtuali), l'oggetto type_info viene scritto insieme alla vtable [...] Per tutti gli altri tipi, scriviamo l'oggetto type_info quando viene usato: quando si applica `typeid 'a un'espressione, lancio di un oggetto o riferimento a un tipo in una clausola catch o in una specifica di eccezione.

E un po 'prima nella stessa pagina:

Se la classe dichiara funzioni virtuali non inline e non pure, la prima viene scelta come "metodo chiave" per la classe e la vtable viene emessa solo nell'unità di traduzione in cui è definito il metodo chiave.

Quindi, questo errore si verifica quando il "metodo chiave" manca della sua definizione, come altre risposte già menzionate.


2
Nel mio caso, avevo una classe base che dichiarava ma non definiva metodi virtuali che non erano puramente virtuali. Una volta che li ho resi virtuali, il che è ciò che intendevo, gli errori del linker sono scomparsi.
Tatiana Racheva,

@TatianaRacheva Grazie! La segnalazione degli errori dal linker è poco utile e per un'interfaccia di grandi dimensioni è molto facile perdere la mancanza di '= 0;' per puro virtuale!
rholmes,

21

Se stai collegando un .so all'altro, un'altra possibilità è la compilazione di "-fvisibility = hidden" in gcc o g ++. Se entrambi i file .so sono stati creati con "-fvisibility = hidden" e il metodo chiave non è nello stesso .so di un'altra delle implementazioni della funzione virtuale, quest'ultima non vedrà la vtable o la typeinfo della prima. Al linker, questa sembra una funzione virtuale non implementata (come nelle risposte di paxdiablo e cdleary).

In questo caso, è necessario fare un'eccezione per la visibilità della classe base con

__attribute__ ((visibility("default")))

nella dichiarazione di classe. Per esempio,

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

Un'altra soluzione, ovviamente, è di non usare "-fvisibility = hidden". Ciò complica le cose per il compilatore e il linker, probabilmente a scapito delle prestazioni del codice.


1
Non è necessario esportare (scoprire) la classe base se è astratta o inutilizzata, solo le funzioni non virtuali, normalmente solo il costruttore. Le classi derivate devono invece essere esportate, se utilizzate.
Chris Huang-Leaver,

sembra un trucco, ma ha risolto i sintomi dalla mia parte. Grazie !
malat,

16

Le risposte precedenti sono corrette, ma questo errore può anche essere causato dal tentativo di utilizzare typeid su un oggetto di una classe che non ha funzioni virtuali. C ++ RTTI richiede una vtable, quindi le classi su cui si desidera eseguire l'identificazione del tipo richiedono almeno una funzione virtuale.

Se si desidera che le informazioni sul tipo funzionino su una classe per la quale non si desidera realmente alcuna funzione virtuale, rendere virtuale il distruttore.


2
Upmodded perché penso che sia più probabile che sia la causa di quel messaggio di errore specifico (al contrario del caso più generale di metodi indefiniti ...)
Alastair,

4
Una cosa a cui mi sono dovuto abituare con SO non si riferisce alle risposte "sopra" poiché l'ordine può cambiare in base ai voti. Di solito non mi riferisco ad altre risposte ora poiché possono anche essere eliminate. La mia convinzione è che le risposte dovrebbero essere indipendenti. Mi riferisco comunque ai nomi degli utenti per l'attribuzione.
paxdiablo,

Puoi usare typeid senza una vtable; vedi la mia risposta per le citazioni dal manuale di gcc.
CesarB,

11

Ho appena trascorso alcune ore su questo errore, e mentre le altre risposte qui mi hanno aiutato a capire cosa stava succedendo, non hanno risolto il mio problema particolare.

Sto lavorando a un progetto che compila usando sia clang++e g++. Non ho riscontrato problemi di collegamento utilizzando clang++, ma ho riscontrato l' undefined reference to 'typeinfo forerrore g++.

Il punto importante: collegare l'ordine con MATTERS g++. Se si elencano le librerie che si desidera collegare in un ordine errato, è possibile ottenere l' typeinfoerrore.

Vedi questa domanda SO per maggiori dettagli sul collegamento dell'ordine con gcc/ g++.


Grazie!!! Ho trascorso più di un giorno a cercare di scoprire perché stavo ricevendo questo errore e nulla ha funzionato fino a quando non ho visto questa risposta e quella a cui ti sei collegato. Grazie mille!!
Irene,

10

Possibili soluzioni per il codice che si occupa delle librerie RTTI e non RTTI:

a) Ricompila tutto con -frtti o -fno-rtti
b) Se a) non è possibile per te, prova quanto segue:

Supponiamo che libfoo sia costruito senza RTTI. Il tuo codice usa libfoo e si compila con RTTI. Se usi una classe (Foo) in libfoo con virtual, è probabile che ti imbatti in un errore di collegamento che dice: typeinfo mancante per la classe Foo.

Definisci un'altra classe (ad esempio FooAdapter) che non ha virtual e inoltrerà le chiamate a Foo che usi.

Compilare FooAdapter in una piccola libreria statica che non utilizza RTTI e dipende solo dai simboli libfoo. Forniscigli un'intestazione e usala invece nel tuo codice (che utilizza RTTI). Poiché FooAdapter non ha alcuna funzione virtuale, non avrà alcun tipo di informazione e sarai in grado di collegare il tuo binario. Se usi molte classi diverse da libfoo, questa soluzione potrebbe non essere conveniente, ma è un inizio.


Questo è stato per me, il collegamento a una libreria con diverse impostazioni RTTI.
palude

6

Analogamente alla precedente discussione RTTI, NO-RTTI, questo problema può verificarsi anche se si utilizza dynamic_cast e non si include il codice oggetto contenente l'implementazione della classe.

Mi sono imbattuto in questo problema costruendo su Cygwin e quindi il porting del codice su Linux. I file di creazione, la struttura delle directory e persino le versioni di gcc (4.8.2) erano identici in entrambi i casi, ma il codice si collegava e funzionava correttamente su Cygwin ma non riusciva a collegarsi su Linux. Red Hat Cygwin ha apparentemente apportato modifiche al compilatore / linker che evitano il requisito di collegamento del codice oggetto.

Il messaggio di errore del linker Linux mi ha indirizzato correttamente alla linea dynamic_cast, ma i messaggi precedenti in questo forum mi hanno fatto cercare implementazioni di funzioni mancanti piuttosto che il problema reale: manca il codice oggetto. La mia soluzione alternativa era sostituire una funzione di tipo virtuale nella classe base e derivata, ad esempio virtual int isSpecialType (), anziché utilizzare dynamic_cast. Questa tecnica evita la necessità di collegare il codice di implementazione dell'oggetto solo per far funzionare correttamente dynamic_cast.


5

Nella classe base (una classe base astratta) dichiari un distruttore virtuale e poiché non puoi dichiarare un distruttore come pura funzione virtuale, o devi definirlo proprio qui nella classe astratta, solo una definizione fittizia come virtual ~ base ( ) {} lo farà, o in una qualsiasi delle classi derivate.

Se non lo fai, finirai con un "simbolo indefinito" al momento del collegamento. Poiché VMT ha una voce per tutte le funzioni virtuali pure con un NULL corrispondente in quanto aggiorna la tabella in base all'implementazione nella classe derivata. Ma per le funzioni non pure ma virtuali, ha bisogno della definizione al momento del collegamento in modo da poter aggiornare la tabella VMT.

Usa c ++ filt per districare il simbolo. Come $ c ++ filt _ZTIN10storageapi8BaseHostE produrrà qualcosa come "typeinfo for storageapi :: BaseHost".


3

Ho avuto molti di questi errori proprio ora. Quello che è successo è che ho diviso una classe di soli file di intestazione in un file di intestazione e un file cpp. Tuttavia, non ho aggiornato il mio sistema di compilazione, quindi il file cpp non è stato compilato. Tra il semplice fatto di avere riferimenti indefiniti alle funzioni dichiarate nell'intestazione ma non implementate, ho ottenuto molti di questi errori typeinfo.

La soluzione consisteva nel rieseguire il sistema di compilazione per compilare e collegare il nuovo file cpp.


3

nel mio caso, ho usato una libreria di terze parti con file header e così file. ho eseguito una sottoclasse di una classe e si è verificato un errore di collegamento in questo modo quando provo a creare un'istanza della mia sottoclasse.

come menzionato da @sergiy, sapendo che potrebbe essere il problema di 'rtti', sono riuscito a aggirare il problema mettendo l'implementazione del costruttore in un file .cpp separato e applicando i flag di compilazione '-fno-rtti' al file . Funziona bene.

poiché non sono ancora del tutto chiaro sull'interno di questo errore di collegamento, non sono sicuro che la mia soluzione sia generale. tuttavia, penso che valga la pena provare prima di provare l'adattatore come indicato da @francois. e, naturalmente, se tutti i codici sorgente sono disponibili (non nel mio caso), meglio ricompilare con '-frtti' se possibile.

un'altra cosa, se scegli di provare la mia soluzione, prova a rendere il file separato il più semplice possibile e non usare alcune fantasiose funzionalità di C ++. presta particolare attenzione alle cose relative al potenziamento, perché molto dipende da RTTI.


2

Ho lo stesso errore quando la mia interfaccia (con tutte le funzioni virtuali pure) aveva bisogno di un'altra funzione e ho dimenticato di "null".

avevo

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

Ultimo vaClose non è virtuale, quindi compilato non sapeva dove ottenere l'implementazione per esso e quindi confuso. il mio messaggio era:

... TCPClient.o :(. Rodata + 0x38): riferimento indefinito a `typeinfo per ICommProvider '

Semplice cambiamento da

virtual int vaClose();

per

virtual int vaClose() = 0;

risolto il problema. spero che sia d'aiuto


1

Incontro una situazione rara, ma ciò può aiutare altri amici in una situazione simile. Devo lavorare su un vecchio sistema con gcc 4.4.7. Devo compilare il codice con supporto c ++ 11 o successivo, quindi creo l'ultima versione di gcc 5.3.0. Quando si crea il mio codice e si collega alle dipendenze se la dipendenza viene compilata con un compilatore più vecchio, ho ricevuto l'errore "riferimento indefinito" anche se ho chiaramente definito il percorso di collegamento con -L / path / to / lib -llibname. Alcuni pacchetti come boost e progetti creati con cmake di solito hanno la tendenza a utilizzare il compilatore più vecchio e di solito causano tali problemi. Devi fare molto per assicurarti che utilizzino il compilatore più recente.


1

Nel mio caso è puramente un problema di dipendenza dalla libreria anche se ho una chiamata Dynamic_cast. Dopo aver aggiunto abbastanza dipendenza nel makefile questo problema era sparito.


0

Verifica che le tue dipendenze siano state compilate senza -f-nortti.

Per alcuni progetti devi impostarlo esplicitamente, come in RocksDB:

USE_RTTI=1 make shared_lib -j4

0

Nel mio caso era una funzione virtuale in una classe di interfaccia che non era definita come un virtuale puro.

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

Ho dimenticato un = 0po '.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.