g ++ referencia indefinida a typeinfo

209

Acabo de encontrar el siguiente error (y encontré la solución en línea, pero no está presente en Stack Overflow):

(.gnu.linkonce. [stuff]): referencia indefinida a [método] [archivo de objeto] :(. gnu.linkonce. [stuff]): referencia indefinida a `typeinfo para [classname] '

¿Por qué podría uno obtener uno de estos errores de vinculador "referencia indefinida a typeinfo"?

(Puntos de bonificación si puede explicar lo que sucede detrás de escena).

cdleary
fuente
31
Sé que es una publicación anterior, pero tuve el mismo problema hoy, y la solución fue simplemente definir mi función virtual como virtual abc () {} en la clase base, en lugar de virtual abc (); que dio el error
Nav
15
mejor aún como virtual void abc() =0;(si la versión base nunca se llama)
dhardy
3
@Nav: Si define abc()así, puede olvidarse fácilmente de redefinir abc()en la clase derivada y pensar que todo está bien, ya que aún puede llamar a la función sin ningún problema. En este artículo se encuentra una buena práctica para implementar funciones virtuales puras , y esto es hacer que la función imprima "Función virtual pura llamada" y luego bloquee el programa.
HelloGoodbye
1
Estaba teniendo el mismo error. He descubierto que cambiar el orden de las referencias a "lib" puede ayudar. acabo de mover las bibliotecas problemáticas desde el principio hasta el final de la lista y esto resolvió el problema
javapowered
2
GAH. Esta es ahora al menos la segunda vez que navego exactamente a esta página, para leer el comentario de @dhardy y decirme 'Doh'. Acabo de pasar 45 minutos tratando de localizar algún comportamiento loco y todo lo que necesitaba era = 0;.
dwanderson

Respuestas:

223

Una posible razón es porque está declarando una función virtual sin definirla.

Cuando lo declara sin definirlo en la misma unidad de compilación, está indicando que está definido en otro lugar; esto significa que la fase de enlace intentará encontrarlo en una de las otras unidades de compilación (o bibliotecas).

Un ejemplo de definición de la función virtual es:

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

En este caso, está adjuntando una definición a la declaración, lo que significa que el vinculador no necesita resolverla más tarde.

La línea

virtual void fn();

declara fn()sin definirlo y provocará el mensaje de error que solicitó.

Es muy similar al código:

extern int i;
int *pi = &i;

que establece que el entero ise declara en otra unidad de compilación que debe resolverse en el momento del enlace (de lo contrario pi, no se puede establecer en su dirección).

paxdiablo
fuente
28
Es incorrecto decir que virtual void fn() = 0es una definición. No es una definición, sino una mera declaración . La única razón por la que el vinculador no está tratando de resolverlo es porque la entrada VMT correspondiente no se referirá a un cuerpo de función (lo más probable es que contenga un puntero nulo). Sin embargo, nadie le prohíbe llamar a esta función virtual pura de una manera no virtual, es decir, utilizando un nombre totalmente calificado. En este caso, el enlazador se busque el cuerpo, y usted tendrá que definir la función. Y sí, puede definir un cuerpo para una función virtual pura.
ANT
1
Y a veces uno incluso debe declarar un cuerpo para una función virtual pura.
marca el
3
El compilador (g ++) le dirá cuál es el símbolo que falta. Nota: en caso de vinculación dinámica de bibliotecas, puede obtener un nombre mutilado. Use c ++ filt <mangledNameVariable> para obtener una forma legible. El error typeinfo con un nombre de clase fue en mi caso debido a una implementación de destructor virtual faltante en alguna clase base.
chmike
1
La pregunta menciona específicamente que falta información de tipo, que tiene que ver con rtti. Ver comentario de Damon en stackoverflow.com/questions/11904519/…
wilsonmichaelpatrick
1
@gbmhunter, bastante justo. Hizo el cambio.
paxdiablo
150

Esto también puede suceder cuando mezclas -fno-rttiy -frtticodificas. Luego debe asegurarse de que cualquier clase, a la que type_infose accede en el -frtticódigo, tenga su método clave compilado -frtti. Tal acceso puede ocurrir cuando crea un objeto de la clase, usa dynamic_castetc.

[ fuente ]

Sergiy Belozorov
fuente
20
MUCHAS GRACIAS. Eso solucionó mi problema después de 5 horas de búsqueda.
steipete
1
el enlace fuente está muerto, seguramente fue lo mismo que permalink.gmane.org/gmane.comp.gcc.help/32475
matemáticas
1
Gracias por señalar esto. La página original todavía está disponible aquí: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov
3
¡StackOverflow.com al rescate nuevamente! Desearía poder votar más de una vez. Después de golpearme la cabeza con el teclado durante una hora, tu respuesta fue lo que necesitaba.
spartygw
1
n + 1 vidas salvadas y aún contando :)
Gabriel
53

Esto ocurre cuando las funciones virtuales declaradas (no puras) son cuerpos faltantes. En su definición de clase, algo como:

virtual void foo();

Debe definirse (en línea o en un archivo fuente vinculado):

virtual void foo() {}

O declarado puro virtual:

virtual void foo() = 0;
cdleary
fuente
27

Citando del manual de gcc :

Para las clases polimórficas (clases con funciones virtuales), el objeto type_info se escribe junto con vtable [...] Para todos los demás tipos, escribimos el objeto type_info cuando se usa: cuando se aplica 'typeid' a una expresión, arrojar un objeto o referirse a un tipo en una cláusula catch o especificación de excepción.

Y un poco antes en la misma página:

Si la clase declara funciones virtuales no en línea, no puras, se elige la primera como el "método clave" para la clase, y la vtable solo se emite en la unidad de traducción donde se define el método clave.

Entonces, este error ocurre cuando el "método clave" no tiene su definición, como otras respuestas ya mencionadas.

CesarB
fuente
2
En mi caso, tenía una clase base que declaraba pero no definía métodos virtuales que no eran virtuales. Una vez que los hice puramente virtual, que es lo que quise decir, los errores del enlazador desaparecieron.
Tatiana Racheva
@TatianaRacheva ¡Gracias! El informe de errores del vinculador no es útil y para una interfaz grande es muy fácil pasar por alto la falta de '= 0;' para puro virtual!
rholmes
21

Si está vinculando un .so a otro, una posibilidad más es compilar con "-fvisibility = hidden" en gcc o g ++. Si ambos archivos .so se compilaron con "-fvisibility = hidden" y el método de la clave no está en el mismo .so que otra de las implementaciones de la función virtual, este último no verá la vtable o typeinfo de la primera. Para el enlazador, esto parece una función virtual no implementada (como en las respuestas de paxdiablo y cdleary).

En este caso, debe hacer una excepción para la visibilidad de la clase base con

__attribute__ ((visibility("default")))

en la declaración de clase. Por ejemplo,

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

Otra solución, por supuesto, es no usar "-visibilidad = oculto". Eso complica las cosas para el compilador y el enlazador, posiblemente en detrimento del rendimiento del código.

humano
fuente
1
No necesita exportar (mostrar) la clase base si es abstracta o no utilizada, solo las funciones no virtuales, normalmente solo el constructor. Las clases derivadas , por otro lado, deben exportarse, si se utilizan.
Chris Huang-Leaver
se siente como un truco, pero resolvió los síntomas de mi lado. Gracias !
malat
16

Las respuestas anteriores son correctas, pero este error también puede ser causado al intentar usar typeid en un objeto de una clase que no tiene funciones virtuales. C ++ RTTI requiere una vtable, por lo que las clases en las que desea realizar la identificación de tipo requieren al menos una función virtual.

Si desea que la información de tipo funcione en una clase para la que realmente no desea ninguna función virtual, haga que el destructor sea virtual.

Tyler McHenry
fuente
2
Actualizado porque creo que es más probable que sea la causa de ese mensaje de error específico (a diferencia del caso más general de métodos indefinidos ...)
Alastair
44
Una cosa a la que tuve que acostumbrarme con SO no es referirme a las respuestas "anteriores" ya que el orden puede cambiar en función de los votos. Por lo general, no me refiero a ninguna otra respuesta ahora, ya que también se pueden eliminar. Mi creencia es que las respuestas deben ser independientes. Sin embargo, todavía me refiero a los nombres de usuario para la atribución.
paxdiablo
Puede usar typeid sin vtable; vea mi respuesta para las citas del manual de gcc.
CesarB
11

Acabo de pasar unas horas en este error, y aunque las otras respuestas aquí me ayudaron a comprender lo que estaba sucediendo, no solucionaron mi problema particular.

Estoy trabajando en un proyecto que compila usando ambos clang++y g++. No estaba teniendo problemas de vinculación clang++, pero recibía el undefined reference to 'typeinfo forerror g++.

El punto importante: vincular el orden IMPORTA con g++. Si enumera las bibliotecas que desea vincular en un orden incorrecto, puede obtener el typeinfoerror.

Consulte esta pregunta SO para obtener más detalles sobre el orden de vinculación con gcc/ g++.

dinkelk
fuente
¡¡¡Gracias!!! Pasé más de un día tratando de averiguar por qué recibía este error y nada funcionó hasta que vi esta respuesta y la que vincularon. ¡¡Muchas gracias!!
Irene
10

Posibles soluciones para el código que se ocupa de las bibliotecas RTTI y no RTTI:

a) Vuelva a compilar todo con -frtti o -fno-rtti
b) Si a) no es posible para usted, intente lo siguiente:

Suponga que libfoo está construido sin RTTI. Su código usa libfoo y se compila con RTTI. Si usa una clase (Foo) en libfoo que tiene virtuales, es probable que se encuentre con un error de tiempo de enlace que dice: falta información de tipo para la clase Foo.

Defina otra clase (por ejemplo, FooAdapter) que no tenga virtual y reenviará las llamadas a Foo que utilice.

Compile FooAdapter en una pequeña biblioteca estática que no use RTTI y solo dependa de símbolos libfoo. Proporcione un encabezado y úselo en su código (que usa RTTI). Como FooAdapter no tiene una función virtual, no tendrá ningún tipo de información y podrá vincular su binario. Si usa muchas clases diferentes de libfoo, esta solución puede no ser conveniente, pero es un comienzo.

Francois
fuente
Esto fue todo para mí, vinculando a una biblioteca con diferentes configuraciones de RTTI.
pantano
6

De manera similar a la discusión de RTTI, NO-RTTI anterior, este problema también puede ocurrir si usa dynamic_cast y no incluye el código objeto que contiene la implementación de la clase.

Me encontré con este problema al construir en Cygwin y luego portar código a Linux. Los archivos make, la estructura de directorios e incluso las versiones de gcc (4.8.2) fueron idénticos en ambos casos, pero el código se vinculó y funcionó correctamente en Cygwin, pero no se pudo vincular en Linux. Red Hat Cygwin aparentemente ha realizado modificaciones de compilador / enlazador que evitan el requisito de vinculación del código objeto.

El mensaje de error del enlazador de Linux me dirigió correctamente a la línea dynamic_cast, pero los mensajes anteriores en este foro me hicieron buscar implementaciones de funciones que faltan en lugar del problema real: el código de objeto que falta. Mi solución alternativa era sustituir una función de tipo virtual en la clase base y derivada, por ejemplo, virtual int isSpecialType (), en lugar de usar dynamic_cast. Esta técnica evita el requisito de vincular el código de implementación del objeto solo para que dynamic_cast funcione correctamente.

FNE
fuente
5

En la clase base (una clase base abstracta) usted declara un destructor virtual y, como no puede declarar un destructor como una función virtual pura, debe definirlo aquí mismo en la clase abstracta, solo una definición ficticia como virtual ~ base ( ) {} lo hará, o en cualquiera de las clases derivadas.

Si no lo hace, terminará en un "símbolo indefinido" en el momento del enlace. Dado que VMT tiene una entrada para todas las funciones virtuales puras con un NULL coincidente, ya que actualiza la tabla dependiendo de la implementación en la clase derivada. Pero para las funciones no puras pero virtuales, necesita la definición en el momento del enlace para que pueda actualizar la tabla VMT.

Use c ++ filt para demanglear el símbolo. Como $ c ++ filt _ZTIN10storageapi8BaseHostE generará algo como "typeinfo for storageapi :: BaseHost".

Prashanth
fuente
3

Recibí muchos de estos errores en este momento. Lo que sucedió es que dividí una clase de solo archivo de encabezado en un archivo de encabezado y un archivo cpp. Sin embargo, no actualicé mi sistema de compilación, por lo que el archivo cpp no ​​se compiló. Entre simplemente tener referencias indefinidas a las funciones declaradas en el encabezado pero no implementadas, obtuve muchos de estos errores typeinfo.

La solución fue volver a ejecutar el sistema de compilación para compilar y vincular el nuevo archivo cpp.

Claudiu
fuente
3

en mi caso, utilicé una biblioteca de terceros con archivos de encabezado y así archivos. Subclasifiqué una clase y se produjo un error de enlace como este cuando intento crear una instancia de mi subclase.

como mencionó @sergiy, sabiendo que podría ser el problema de 'rtti', logré solucionarlo poniendo la implementación del constructor en un archivo .cpp separado y aplicando indicadores de compilación '-fno-rtti' al archivo . funciona bien.

Como todavía no tengo muy claro el error interno de este enlace, no estoy seguro de si mi solución es general. Sin embargo, creo que vale la pena intentarlo antes de probar el adaptador como lo menciona @francois. y, por supuesto, si todos los códigos fuente están disponibles (no en mi caso), mejor compile con '-frtti' si es posible.

Una cosa más, si elige probar mi solución, intente hacer que el archivo separado sea lo más simple posible, y no use algunas características sofisticadas de C ++. preste especial atención a las cosas relacionadas con el impulso, porque gran parte depende de rtti.

Uwydoc
fuente
2

Tengo el mismo error cuando mi interfaz (con todas las funciones virtuales puras) necesitaba una función más y olvidé "anularla".

yo tenía

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

El último vaClose no es virtual, por lo que compilado no sabía dónde obtener la implementación y, por lo tanto, se confundió. mi mensaje fue:

... TCPClient.o :(. Rodata + 0x38): referencia indefinida a `typeinfo para ICommProvider '

Cambio simple de

virtual int vaClose();

a

virtual int vaClose() = 0;

Se solucionó el problema. Espero eso ayude

Alex Paniutin
fuente
1

Me encuentro con una situación que es rara, pero esto puede ayudar a otros amigos en una situación similar. Tengo que trabajar en un sistema anterior con gcc 4.4.7. Tengo que compilar código con soporte para c ++ 11 o superior, así que construyo la última versión de gcc 5.3.0. Al compilar mi código y vincularme a las dependencias si la dependencia se compila con un compilador más antiguo, recibí un error de 'referencia indefinida a' aunque definí claramente la ruta de enlace con -L / ruta / a / lib -llibname. Algunos paquetes como boost y proyectos compilados con cmake generalmente tienden a usar el compilador anterior, y generalmente causan tales problemas. Tienes que recorrer un largo camino para asegurarte de que usan el compilador más nuevo.

Kemin Zhou
fuente
1

En mi caso, es puramente un problema de dependencia de la biblioteca, incluso si tengo una llamada dynamic_cast. Después de agregar suficiente dependencia al archivo MAKE, este problema desapareció.

Charlie
fuente
0

Compruebe que sus dependencias fueron compiladas sin -f-nortti.

Para algunos proyectos, debe configurarlo explícitamente, como en RocksDB:

USE_RTTI=1 make shared_lib -j4
Vitaly Isaev
fuente
0

En mi caso, era una función virtual en una clase de interfaz que no se definía como puramente virtual.

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

Olvidé la = 0parte.

la piel de gallina
fuente