Referência indefinida do g ++ para typeinfo

209

Acabei de encontrar o seguinte erro (e encontrei a solução online, mas ela não está presente no Stack Overflow):

(.gnu.linkonce. [coisas]): referência indefinida a [método] [arquivo de objeto] :(. gnu.linkonce. [coisas]): referência indefinida a `typeinfo for [classname] '

Por que alguém pode obter um desses erros de vinculador "referência indefinida ao typeinfo"?

(Pontos de bônus se você puder explicar o que está acontecendo nos bastidores.)

cdleary
fonte
31
Eu sei que é uma postagem antiga, mas tive o mesmo problema hoje, e a solução foi simplesmente definir minha função virtual como virtual abc () {} na classe base, em vez de virtual abc (); que deu o erro.
Nav
15
ainda melhor como virtual void abc() =0;(se a versão base nunca é chamado)
dhardy
3
@ Nav: Se você definir abc()assim, poderá facilmente esquecer de redefinir abc()na classe derivada e pensar que está tudo bem, pois você ainda poderá chamar a função sem nenhum problema. Uma boa prática para implementar funções virtuais puras é encontrada neste artigo , e é para fazer a função imprimir "Função virtual pura chamada" e travar o programa.
HelloGoodbye 27/09/12
1
Eu estava tendo o mesmo erro. Eu descobri que alterar a ordem das referências a "lib" pode ajudar. Acabei de me mudar problema lib da do início ao fim da lista e isso resolveu o problema
javapowered
2
GAH. Agora é a segunda vez que navego exatamente nesta página, para ler o comentário de @dhardy e dizer para mim mesmo 'Doh'. Passei 45 minutos tentando rastrear algum comportamento louco e tudo que eu precisava era = 0;.
precisa saber é o seguinte

Respostas:

223

Uma razão possível é porque você está declarando uma função virtual sem defini-la.

Quando você o declara sem defini-lo na mesma unidade de compilação, indica que ele está definido em outro lugar - isso significa que a fase do vinculador tentará encontrá-lo em uma das outras unidades de compilação (ou bibliotecas).

Um exemplo de definição da função virtual é:

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

Nesse caso, você está anexando uma definição à declaração, o que significa que o vinculador não precisa resolvê-la posteriormente.

A linha

virtual void fn();

declara fn() sem defini-lo e causará a mensagem de erro sobre a qual você perguntou.

É muito parecido com o código:

extern int i;
int *pi = &i;

que indica que o número inteiro ié declarado em outra unidade de compilação que deve ser resolvida no momento do link (caso contrário, pinão pode ser definido como seu endereço).

paxdiablo
fonte
28
É incorreto dizer que virtual void fn() = 0é uma definição. Não é uma definição, mas uma mera declaração . O único motivo pelo qual o vinculador não está tentando resolvê-lo é que a entrada VMT correspondente não fará referência a um corpo de função (provavelmente conterá um ponteiro nulo). No entanto, ninguém o proíbe de chamar essa função virtual pura de maneira não virtual, ou seja, usando um nome totalmente qualificado. Neste caso, o vinculador irá olhar para o corpo, e você terá que definir a função. E sim, você pode definir um corpo para uma função virtual pura.
AnT
1
E, às vezes, é preciso declarar um corpo para uma função virtual pura.
marca de
3
O compilador (g ++) dirá qual é o símbolo ausente. Nota: No caso de vinculação de biblioteca dinâmica, você pode obter um nome mutilado. Use c ++ filt <mangledNameVariable> para obtê-lo em um formato legível. O erro typeinfo com um nome de classe ocorreu no meu caso devido a uma implementação de destruidor virtual ausente em alguma classe base.
chmike
1
A pergunta menciona especificamente que está faltando typeinfo, que tem a ver com rtti. Veja o comentário de Damon em stackoverflow.com/questions/11904519/…
wilsonmichaelpatrick
1
@gbmhunter, justo o suficiente. Fez a mudança.
paxdiablo
150

Isso também pode acontecer quando você mistura -fno-rttie -frtticodifica. Então você precisa garantir que qualquer classe, que type_infoé acessada no -frtticódigo, tenha seu método-chave compilado -frtti. Esse acesso pode acontecer quando você cria um objeto da classe, usedynamic_cast etc.

[ fonte ]

Sergiy Belozorov
fonte
20
MUITO OBRIGADO. Isso corrigiu meu problema após 5 horas de pesquisa.
steipete
1
o link de origem está morto, certamente era o mesmo que permalink.gmane.org/gmane.comp.gcc.help/32475
math
1
Obrigado por apontar isso. A página original ainda está disponível aqui: web.archive.org/web/20100503172629/http://www.pubbs.net/201004/…
Sergiy Belozorov
3
StackOverflow.com para o resgate novamente! Eu gostaria de poder votar mais de uma vez. Depois de bater minha cabeça no teclado por uma hora, sua resposta foi o que eu precisava.
Spartygw
1
n + 1 vidas salvas e ainda contando :)
Gabriel
53

Isso ocorre quando funções virtuais declaradas (não puras) estão em falta de corpos. Na sua definição de classe, algo como:

virtual void foo();

Deve ser definido (em linha ou em um arquivo de origem vinculado):

virtual void foo() {}

Ou declarado virtual puro:

virtual void foo() = 0;
cdleary
fonte
27

Citando o manual do gcc :

Para classes polimórficas (classes com funções virtuais), o objeto type_info é gravado junto com a vtable. [...] Para todos os outros tipos, gravamos o objeto type_info quando é usado: ao aplicar o `typeid 'a uma expressão, jogando um objeto ou referindo-se a um tipo em uma cláusula catch ou especificação de exceção.

E um pouco mais cedo na mesma página:

Se a classe declarar funções virtuais não-inline e não puras, a primeira será escolhida como o “método principal” para a classe, e a tabela v é emitida apenas na unidade de tradução onde o método principal é definido.

Portanto, esse erro ocorre quando o "método-chave" está ausente em sua definição, como outras respostas já mencionadas.

CesarB
fonte
2
No meu caso, eu tinha uma classe base que declarou, mas não definiu, métodos virtuais que não eram puramente virtuais. Depois que eu os tornei virtuais, o que eu quis dizer com isso, os erros do vinculador desapareceram.
Tatiana Racheva
@TatianaRacheva Thanks! O relatório de erros do vinculador é pouco útil e, para uma interface grande, é muito fácil perder a falta do '= 0;' para virtual puro!
Rholmes 15/05
21

Se você está vinculando um .so a outro, mais uma possibilidade é compilar com "-fvisibility = hidden" no gcc ou no g ++. Se os dois arquivos .so foram criados com "-fvisibility = hidden" e o método-chave não é o mesmo. Assim como outra das implementações da função virtual, o último não verá a tabela ou o tipo de informação da primeira. Para o vinculador, isso parece uma função virtual não implementada (como nas respostas de paxdiablo e cdleary).

Nesse caso, você deve fazer uma exceção para a visibilidade da classe base com

__attribute__ ((visibility("default")))

na declaração de classe. Por exemplo,

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

Outra solução, é claro, é não usar "-fvisibility = hidden". Isso complica as coisas para o compilador e o vinculador, possivelmente em detrimento do desempenho do código.

humano
fonte
1
Você não precisa exportar (reexibir) a classe base, se for abstrata ou não utilizada, apenas as funções não virtuais, normalmente apenas o construtor. As classes derivadas , por outro lado, devem ser exportadas, se forem usadas.
Chris Huang-Leaver
parece um hack, mas resolveu os sintomas do meu lado. Obrigado !
malat 28/07/19
16

As respostas anteriores estão corretas, mas esse erro também pode ser causado pela tentativa de usar typeid em um objeto de uma classe que não possui funções virtuais. O C ++ RTTI requer uma vtable, portanto, as classes nas quais você deseja executar a identificação de tipo exigem pelo menos uma função virtual.

Se você deseja que as informações de tipo trabalhem em uma classe para a qual você realmente não deseja nenhuma função virtual, torne o destruidor virtual.

Tyler McHenry
fonte
2
Upmodded porque eu acho que isso é mais provável que seja a causa dessa mensagem de erro específica (ao contrário do caso mais geral de métodos indefinidos ...)
Alastair
4
Uma coisa que eu tive que me acostumar com o SO não está se referindo às respostas "acima", pois a ordem pode mudar com base nos votos. Normalmente, não me refiro a outras respostas agora, pois elas também podem ser excluídas. Minha crença é que as respostas devem ser independentes. Ainda me refiro a nomes de usuários para atribuição.
21468
Você pode usar typeid sem uma vtable; veja minha resposta para as citações no manual do gcc.
CesarB
11

Passei algumas horas com esse erro e, enquanto as outras respostas aqui me ajudaram a entender o que estava acontecendo, elas não resolveram o meu problema específico.

Estou trabalhando em um projeto que compila usando ambos clang++e g++. Eu não estava tendo problemas de vinculação usando clang++, mas estava recebendo o undefined reference to 'typeinfo forerro com g++.

O ponto importante: vincular a ordem MATTERS com g++. Se você listar as bibliotecas que deseja vincular em uma ordem incorreta, poderá obter otypeinfo erro.

Consulte esta pergunta SO para obter mais detalhes sobre como vincular ordem com gcc/ g++.

dinkelk
fonte
Obrigado!!! Passei mais de um dia tentando descobrir por que estava recebendo esse erro e nada funcionou até ver a resposta e a que você vinculou. Muito obrigado!!
Irene
10

Soluções possíveis para código que lida com bibliotecas RTTI e não RTTI:

a) Recompile tudo com -frtti ou -fno-rtti
b) Se a) não for possível para você, tente o seguinte:

Suponha que o libfoo seja construído sem o RTTI. Seu código usa libfoo e compila com RTTI. Se você usar uma classe (Foo) no libfoo que possui virtuais, é provável que você encontre um erro de tempo de link que diz: typeinfo ausente para a classe Foo.

Defina outra classe (por exemplo, FooAdapter) que não possui virtual e encaminhará as chamadas para o Foo que você usa.

Compile o FooAdapter em uma pequena biblioteca estática que não usa RTTI e depende apenas dos símbolos libfoo. Forneça um cabeçalho para ele e use-o no seu código (que usa RTTI). Como o FooAdapter não tem função virtual, ele não terá nenhuma informação de tipo e você poderá vincular seu binário. Se você usa muitas classes diferentes do libfoo, esta solução pode não ser conveniente, mas é um começo.

François
fonte
Era isso para mim, vinculando a uma biblioteca com diferentes configurações de RTTI.
pântano
6

De maneira semelhante à discussão sobre RTTI, NO-RTTI acima, esse problema também pode ocorrer se você usar dynamic_cast e não incluir o código do objeto que contém a implementação da classe.

Encontrei este problema com base no Cygwin e depois portando código para o Linux. Os arquivos make, a estrutura de diretórios e até as versões gcc (4.8.2) eram idênticas nos dois casos, mas o código vinculava e operava corretamente no Cygwin, mas não conseguiu vincular no Linux. Aparentemente, o Red Hat Cygwin fez modificações no compilador / vinculador que evitam o requisito de vinculação do código do objeto.

A mensagem de erro do vinculador do Linux me direcionou corretamente para a linha dynamic_cast, mas as mensagens anteriores deste fórum me fizeram procurar por implementações de funções ausentes e não pelo problema real: código de objeto ausente. Minha solução alternativa foi substituir uma função de tipo virtual na classe base e derivada, por exemplo, virtual int isSpecialType (), em vez de usar dynamic_cast. Essa técnica evita o requisito de vincular o código de implementação de objeto apenas para fazer com que dynamic_cast funcione corretamente.

FNE
fonte
5

Na classe base (uma classe base abstrata), você declara um destruidor virtual e, como não pode declarar um destruidor como uma função virtual pura, é necessário defini-lo aqui na classe abstrata, apenas uma definição fictícia como virtual ~ base ( ) {} fará ou em qualquer classe derivada.

Se você não conseguir fazer isso, você terminará em um "símbolo indefinido" no momento do link. Como o VMT tem uma entrada para todas as funções virtuais puras com um NULL correspondente, ele atualiza a tabela dependendo da implementação na classe derivada. Mas para as funções não puras, mas virtuais, ele precisa da definição no momento do link para poder atualizar a tabela VMT.

Use c ++ filt para desmantelar o símbolo. Como $ c ++ filt _ZTIN10storageapi8BaseHostE, será exibido algo como "typeinfo para storageapi :: BaseHost".

Prashanth
fonte
3

Eu tenho muitos desses erros agora. O que aconteceu é que eu dividi uma classe somente de arquivo de cabeçalho em um arquivo de cabeçalho e um arquivo cpp. No entanto, não atualizei meu sistema de compilação, portanto o arquivo cpp não foi compilado. Entre simplesmente ter referências indefinidas para as funções declaradas no cabeçalho, mas não implementadas, recebi muitos desses erros typeinfo.

A solução foi executar novamente o sistema de compilação para compilar e vincular o novo arquivo cpp.

Claudiu
fonte
3

no meu caso, usei uma biblioteca de terceiros com arquivos de cabeçalho e, portanto, arquivo. subclassifiquei uma classe e erro de link como esse ocorreu quando tento instanciar minha subclasse.

como mencionado por @sergiy, sabendo que poderia ser o problema do 'rtti', consegui contorná-lo colocando a implementação do construtor em um arquivo .cpp separado e aplicando sinalizadores de compilação '-fno-rtti' ao arquivo . isso funciona bem.

Como ainda não estou muito claro sobre o erro interno deste link, não tenho certeza se minha solução é geral. no entanto, acho que vale a pena tentar antes de tentar o adaptador conforme mencionado por @francois. e, é claro, se todos os códigos-fonte estiverem disponíveis (não no meu caso), é melhor recompilar com '-frtti', se possível.

mais uma coisa, se você optar por tentar minha solução, tente tornar o arquivo separado o mais simples possível e não use alguns recursos sofisticados do C ++. tenha atenção especial em coisas relacionadas ao impulso, porque muito disso depende do rtti.

uwydoc
fonte
2

Eu tenho o mesmo erro quando minha interface (com todas as funções virtuais puras) precisava de mais uma função e eu esqueci de "anulá-la".

eu tinha

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

O último vaClose não é virtual; portanto, o compilado não sabia onde obter a implementação e, portanto, ficou confuso. minha mensagem foi:

... TCPClient.o :(. Rodata + 0x38): referência indefinida para `typeinfo for ICommProvider '

Mudança simples de

virtual int vaClose();

para

virtual int vaClose() = 0;

corrigiu o problema. espero que ajude

Alex Paniutin
fonte
1

Encontro uma situação rara, mas isso pode ajudar outros amigos em situações semelhantes. Eu tenho que trabalhar em um sistema mais antigo com o gcc 4.4.7. Eu tenho que compilar o código com suporte ao c ++ 11 ou superior, então construo a versão mais recente do gcc 5.3.0. Ao criar meu código e vincular-me às dependências, se a dependência for criada com o compilador mais antigo, recebi o erro 'referência indefinida', mesmo que tenha definido claramente o caminho de vinculação com -L / path / to / lib -llibname. Alguns pacotes, como o boost e os projetos criados com o cmake, geralmente tendem a usar o compilador mais antigo e geralmente causam esses problemas. Você precisa percorrer um longo caminho para garantir que eles usem o compilador mais recente.

Kemin Zhou
fonte
1

No meu caso, é puramente um problema de dependência de biblioteca, mesmo que eu tenha uma chamada dynamic_cast. Após adicionar dependência suficiente ao makefile, esse problema desapareceu.

Charlie
fonte
0

Verifique se suas dependências foram compiladas sem -f-nortti.

Para alguns projetos, você deve defini-lo explicitamente, como no RocksDB:

USE_RTTI=1 make shared_lib -j4
Vitaly Isaev
fonte
0

No meu caso, era uma função virtual em uma classe de interface que não foi definida como um virtual puro.

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

Eu esqueci um = 0pouco.

Goosebumps
fonte