c++ - símbolos - O que é uma referência indefinida/erro de símbolo externo não resolvido e como corrigi-lo?




undefined reference to error in c (20)

Quando seus caminhos de inclusão são diferentes

Os erros do vinculador podem ocorrer quando um arquivo de cabeçalho e sua biblioteca compartilhada associada (arquivo .lib) ficam fora de sincronia. Deixe-me explicar.

Como os linkers funcionam? O vinculador corresponde a uma declaração de função (declarada no cabeçalho) com sua definição (na biblioteca compartilhada) comparando suas assinaturas. Você pode obter um erro de vinculador se o vinculador não encontrar uma definição de função que corresponda perfeitamente.

É possível ainda obter um erro de vinculador mesmo que a declaração e a definição pareçam corresponder? Sim! Eles podem parecer iguais no código-fonte, mas realmente depende do que o compilador vê. Essencialmente, você pode acabar com uma situação como esta:

// header1.h
typedef int Number;
void foo(Number);

// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically

Note como as duas declarações de função parecem idênticas no código-fonte, mas elas são realmente diferentes de acordo com o compilador.

Você pode perguntar como alguém acaba em uma situação como essa? Inclua caminhos, é claro! Se ao compilar a biblioteca compartilhada, o caminho de inclusão levar header1.he você acabar usando header2.hem seu próprio programa, você ficará coçando seu cabeçalho imaginando o que aconteceu (trocadilho intencional).

Um exemplo de como isso pode acontecer no mundo real é explicado abaixo.

Mais elaboração com um exemplo

Eu tenho dois projetos: graphics.libe main.exe. Ambos os projetos dependem common_math.h. Suponha que a biblioteca exporte a seguinte função:

// graphics.lib    
#include "common_math.h" 

void draw(vec3 p) { ... } // vec3 comes from common_math.h

E então você vai em frente e inclui a biblioteca em seu próprio projeto.

// main.exe
#include "other/common_math.h"
#include "graphics.h"

int main() {
    draw(...);
}

Estrondo!Você recebe um erro de linker e não faz ideia do motivo da falha. A razão é que a biblioteca comum usa diferentes versões do mesmo include common_math.h(eu deixei claro aqui no exemplo incluindo um caminho diferente, mas pode nem sempre ser tão óbvio. Talvez o caminho de inclusão seja diferente nas configurações do compilador) .

Observe, neste exemplo, que o vinculador diria que não foi possível encontrá-lo draw(), quando, na realidade, você sabe que obviamente ele está sendo exportado pela biblioteca. Você pode passar horas coçando a cabeça imaginando o que deu errado. O problema é que o vinculador vê uma assinatura diferente porque os tipos de parâmetro são um pouco diferentes. No exemplo, vec3é um tipo diferente em ambos os projetos no que diz respeito ao compilador. Isso pode acontecer porque eles vêm de dois arquivos de inclusão um pouco diferentes (talvez os arquivos de inclusão venham de duas versões diferentes da biblioteca).

Depurando o vinculador

DUMPBIN é seu amigo, se você estiver usando o Visual Studio. Tenho certeza que outros compiladores possuem outras ferramentas similares.

O processo é assim:

  1. Observe o estranho nome desconfigurado dado no erro do linker. (por exemplo, desenhar @ gráficos @ XYZ).
  2. Despeje os símbolos exportados da biblioteca em um arquivo de texto.
  3. Procure o símbolo exportado de interesse e observe que o nome desconfigurado é diferente.
  4. Preste atenção em porque os nomes mutilados acabaram sendo diferentes. Você seria capaz de ver que os tipos de parâmetro são diferentes, mesmo que pareçam iguais no código-fonte.
  5. Razão pela qual eles são diferentes. No exemplo dado acima, eles são diferentes devido a diferentes arquivos de inclusão.

[1] Por projeto quero dizer um conjunto de arquivos de origem que estão ligados entre si para produzir uma biblioteca ou um executável.

EDITAR 1: Reescreva a primeira seção para ser mais fácil de entender. Por favor, comente abaixo para me informar se algo precisa ser corrigido. Obrigado!

O que são referências indefinidas / erros de símbolos externos não resolvidos? Quais são as causas comuns e como corrigi-las / preveni-las?

Sinta-se à vontade para editar / adicionar o seu próprio.


Membros da classe:

Um destruidor virtual puro precisa de uma implementação.

Declarar um destrutor puro ainda requer que você o defina (ao contrário de uma função normal):

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition

Isso acontece porque os destruidores da classe base são chamados quando o objeto é destruído implicitamente, portanto, é necessária uma definição.

virtual métodos virtual devem ser implementados ou definidos como puros.

Isso é semelhante a métodos não virtual sem definição, com o argumento adicional de que a declaração pura gera uma tabela fictícia e você pode obter o erro do vinculador sem usar a função:

struct X
{
    virtual void foo();
};
struct Y : X
{
   void foo() {}
};
int main()
{
   Y y; //linker error although there was no call to X::foo
}

Para isso funcionar, declare X::foo() como puro:

struct X
{
    virtual void foo() = 0;
};

Membros da classe não virtual

Alguns membros precisam ser definidos mesmo se não forem usados ​​explicitamente:

struct A
{ 
    ~A();
};

O seguinte resultaria no erro:

A a;      //destructor undefined

A implementação pode ser inline, na própria definição da classe:

struct A
{ 
    ~A() {}
};

ou fora:

A::~A() {}

Se a implementação estiver fora da definição de classe, mas em um cabeçalho, os métodos deverão ser marcados como inline para evitar uma definição múltipla.

Todos os métodos de membro usados ​​precisam ser definidos se usados.

Um erro comum é esquecer de qualificar o nome:

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}

A definição deve ser

void A::foo() {}

membros de dados static devem ser definidos fora da classe em uma única unidade de tradução :

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

Um inicializador pode ser fornecido para um membro de dados const static de integral ou tipo de enumeração dentro da definição de classe; no entanto, o uso do odr desse membro ainda exigirá uma definição de escopo de namespace conforme descrito acima. O C ++ 11 permite a inicialização dentro da classe para todos os membros de dados static const .


Befriending templates ...

Dado o trecho de código de um tipo de modelo com um operador de amigo (ou função);

template <typename T>
class Foo {
    friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};

O operator<<está sendo declarado como uma função não-modelo. Para cada tipo Tusado com Foo, deve haver um não-modelado operator<<. Por exemplo, se houver um tipo Foo<int>declarado, então deve haver uma implementação de operador da seguinte maneira;

std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}

Como não está implementado, o vinculador não consegue encontrá-lo e resulta no erro.

Para corrigir isso, você pode declarar um operador de modelo antes do Footipo e, em seguida, declarar como um amigo, a instanciação apropriada. A sintaxe é um pouco desajeitada, mas é a seguinte;

// forward declare the Foo
template <typename>
class Foo;

// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&);

template <typename T>
class Foo {
    friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
    // note the required <>        ^^^^
    // ...
};

template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
  // ... implement the operator
}

O código acima limita a amizade do operador à instanciação correspondente de Foo, ou seja, a operator<< <int>instanciação é limitada para acessar os membros privados da instanciação de Foo<int>.

Alternativas incluem;

  • Permitindo que a amizade se estenda a todas as instanciações dos templates, como segue;

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
  • Ou, a implementação para o operator<<pode ser feita inline dentro da definição de classe;

    template <typename T>
    class Foo {
        friend std::ostream& operator<<(std::ostream& os, const Foo& a)
        { /*...*/ }
        // ...
    };

Observe que , quando a declaração do operador (ou função) aparece apenas na classe, o nome não está disponível para pesquisa "normal", apenas para pesquisa dependente de argumento, a partir de cppreference ;

Um nome primeiro declarado em uma declaração de amigo dentro de classe ou modelo de classe X torna-se um membro do namespace delimitador interno de X, mas não é acessível para pesquisa (exceto pesquisa dependente de argumento que considera X) a menos que uma declaração correspondente no escopo de namespace seja forneceu...

Há mais leituras sobre modelos de amigos na cppreference e sobre o C ++ FAQ .

Listagem de código mostrando as técnicas acima .

Como uma nota lateral para o exemplo de código com falha; g ++ avisa sobre isso da seguinte maneira

warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]

note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)


Limpar e reconstruir

Uma "limpeza" da compilação pode remover a "madeira morta" que pode ser deixada em torno de compilações anteriores, construções com falha, compilações incompletas e outros problemas de compilação relacionados ao sistema de compilação.

Em geral, o IDE ou compilação incluirá alguma forma de função "limpa", mas ela pode não estar configurada corretamente (por exemplo, em um makefile manual) ou pode falhar (por exemplo, os binários intermediários ou resultantes são somente leitura).

Uma vez concluída a "limpeza", verifique se o "limpo" foi bem-sucedido e se todo o arquivo intermediário gerado (por exemplo, um makefile automatizado) foi removido com êxito.

Esse processo pode ser visto como um recurso final, mas geralmente é um bom primeiro passo ; especialmente se o código relacionado ao erro foi adicionado recentemente (localmente ou do repositório de origem).


Um bug no compilador / IDE

Recentemente, tive esse problema e descobri que era um bug no Visual Studio Express 2013 . Eu tive que remover um arquivo de origem do projeto e adicioná-lo novamente para superar o bug.

Passos para tentar se você acredita que pode ser um bug no compilador / IDE:

  • Limpe o projeto (alguns IDEs têm a opção de fazer isso, você também pode fazê-lo manualmente excluindo os arquivos de objetos)
  • Tente iniciar um novo projeto, copiando todo o código-fonte do original.

Use o vinculador para ajudar a diagnosticar o erro

A maioria dos linkers modernos inclui uma opção detalhada que é impressa em graus variados;

  • Invocação de link (linha de comando),
  • Dados sobre quais bibliotecas estão incluídas no estágio de link,
  • A localização das bibliotecas,
  • Caminhos de pesquisa usados.

Para gcc e clang; você normalmente adicionaria -v -Wl,--verboseou -v -Wl,-và linha de comando. Mais detalhes podem ser encontrados aqui;

Para o MSVC, /VERBOSE(em particular /VERBOSE:LIB) é adicionado à linha de comando do link.


Falta "extern" nas constdeclarações / definições de variáveis ​​(somente C ++)

Para pessoas vindas de C, pode ser uma surpresa que em C ++ as constvariáveis globais possuam uma ligação interna (ou estática). Em C, esse não era o caso, já que todas as variáveis ​​globais são implicitamente extern(isto é, quando a staticpalavra-chave está ausente).

Exemplo:

// file1.cpp
const int test = 5;    // in C++ same as "static const int test = 5"
int test2 = 5;

// file2.cpp
extern const int test;
extern int test2;

void foo()
{
 int x = test;   // linker error in C++ , no error in C
 int y = test2;  // no problem
}

correto seria usar um arquivo de cabeçalho e incluí-lo em file2.cpp e file1.cpp

extern const int test;
extern int test2;

Alternativamente, pode-se declarar a constvariável em file1.cpp com explícitoextern


A ordem em que as bibliotecas vinculadas interdependentes são especificadas está errada.

A ordem na qual as bibliotecas são vinculadas NÃO importa se as bibliotecas dependem umas das outras. Em geral, se a biblioteca A depender da biblioteca B , então a libA DEVE aparecer antes de libB nas flags do linker.

Por exemplo:

// B.h
#ifndef B_H
#define B_H

struct B {
    B(int);
    int x;
};

#endif

// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}

// A.h
#include "B.h"

struct A {
    A(int x);
    B b;
};

// A.cpp
#include "A.h"

A::A(int x) : b(x) {}

// main.cpp
#include "A.h"

int main() {
    A a(5);
    return 0;
};

Crie as bibliotecas:

$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o 
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o 
ar: creating libB.a
a - B.o

Compilar:

$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out

Então, para repetir novamente, a ordem importa!


Falha ao vincular-se a bibliotecas / arquivos de objetos apropriados ou compilar arquivos de implementação

Comumente, cada unidade de tradução gerará um arquivo de objeto que contém as definições dos símbolos definidos naquela unidade de tradução. Para usar esses símbolos, você precisa vincular-se a esses arquivos de objeto.

No gcc, você deve especificar todos os arquivos de objetos que devem ser vinculados na linha de comando ou compilar os arquivos de implementação juntos.

g++ -o test objectFile1.o objectFile2.o -lLibraryName

O libraryName aqui é apenas o nome da biblioteca, sem adições específicas da plataforma. Então, por exemplo, em arquivos de biblioteca do Linux são geralmente chamados de libfoo.so mas você só escreveria -lfoo . No Windows, esse mesmo arquivo pode ser chamado de foo.lib , mas você usaria o mesmo argumento. Você pode ter que adicionar o diretório onde esses arquivos podem ser encontrados usando -L‹directory› . Certifique-se de não escrever um espaço depois de -l ou -L .

Para XCode : Adicione os Caminhos de Pesquisa do Cabeçalho do Usuário -> adicionar o Caminho de Pesquisa da Biblioteca -> arraste e solte a referência da biblioteca real na pasta do projeto.

Em MSVS , os arquivos adicionados a um projeto automaticamente têm seus arquivos de objeto vinculados e um arquivo lib seria gerado (no uso comum). Para usar os símbolos em um projeto separado, você precisaria incluir os arquivos lib nas configurações do projeto. Isso é feito na seção Vinculador das propriedades do projeto, em Input -> Additional Dependencies . (o caminho para o arquivo lib deve ser incluído no Linker -> General -> Additional Library Directories ) Ao usar uma biblioteca de terceiros que é fornecida com um arquivo lib , a falha em fazê-lo geralmente resulta no erro.

Também pode acontecer de você esquecer de adicionar o arquivo à compilação, caso em que o arquivo de objeto não será gerado. No gcc, você adicionaria os arquivos à linha de comando. No MSVS, adicionar o arquivo ao projeto fará com que ele seja compilado automaticamente (embora os arquivos possam, manualmente, ser excluídos individualmente da compilação).

Na programação do Windows, o sinal de que você não __imp_ uma biblioteca necessária é que o nome do símbolo não resolvido começa com __imp_ . Procure o nome da função na documentação e ela deve dizer qual biblioteca você precisa usar. Por exemplo, o MSDN coloca as informações em uma caixa na parte inferior de cada função em uma seção chamada "Biblioteca".


Implementações de modelo não visíveis.

Os modelos não especializados devem ter suas definições visíveis para todas as unidades de tradução que os utilizam. Isso significa que você não pode separar a definição de um modelo para um arquivo de implementação. Se você deve separar a implementação, a solução usual é ter um arquivo impl que você inclua no final do cabeçalho que declara o modelo. Uma situação comum é:

template<class T>
struct X
{
    void foo();
};

int main()
{
    X<int> x;
    x.foo();
}

//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}

Para corrigir isso, você deve mover a definição de X::foo para o arquivo de cabeçalho ou algum local visível para a unidade de tradução que o utiliza.

Modelos especializados podem ser implementados em um arquivo de implementação e a implementação não precisa ser visível, mas a especialização deve ser declarada anteriormente.

Para mais explicações e outra solução possível (instanciação explícita), veja esta pergunta e resposta .


Os símbolos foram definidos em um programa C e usados ​​no código C ++.

A função (ou variável) void foo() foi definida em um programa C e você tenta usá-la em um programa C ++:

void foo();
int main()
{
    foo();
}

O vinculador C ++ espera que os nomes sejam desconfigurados, portanto, você precisa declarar a função como:

extern "C" void foo();
int main()
{
    foo();
}

Equivalente, em vez de ser definido em um programa C, a função (ou variável) void foo() foi definida em C ++, mas com ligação C:

extern "C" void foo();

e você tenta usá-lo em um programa C ++ com ligação C ++.

Se uma biblioteca inteira estiver incluída em um arquivo de cabeçalho (e foi compilada como código C); o include precisará ser o seguinte;

extern "C" {
    #include "cheader.h"
}

A Microsoft oferece um # #pragma para referenciar a biblioteca correta no momento do link;

#pragma comment(lib, "libname.lib")

Além do caminho da biblioteca, incluindo o diretório da biblioteca, esse deve ser o nome completo da biblioteca.


Além disso, se você estiver usando bibliotecas de terceiros, verifique se possui os binários de 32/64 bits corretos


Esta é uma das mensagens de erro mais confusas que todos os programadores de VC ++ têm visto uma e outra vez. Vamos esclarecer as coisas primeiro.

A. O que é símbolo? Em suma, um símbolo é um nome. Pode ser um nome de variável, um nome de função, um nome de classe, um nome typedef ou qualquer coisa, exceto os nomes e sinais que pertencem à linguagem C ++. É definido pelo usuário ou introduzido por uma biblioteca de dependências (outra definida pelo usuário).

B. O que é externo? No VC ++, cada arquivo de origem (.cpp, .c, etc.) é considerado como uma unidade de conversão, o compilador compila uma unidade por vez e gera um arquivo de objeto (.obj) para a unidade de tradução atual. (Observe que todos os arquivos de cabeçalho que este arquivo de origem incluiu serão pré-processados ​​e serão considerados como parte desta unidade de tradução). Tudo dentro de uma unidade de tradução é considerado interno, todo o restante é considerado externo. Em C ++, você pode fazer referência a um símbolo externo usando palavras-chave como extern , __declspec (dllimport) e assim por diante.

C. O que é "resolver"? Resolver é um termo de tempo de vinculação. No tempo de vinculação, o vinculador tenta localizar a definição externa para cada símbolo nos arquivos de objeto que não podem encontrar sua definição internamente. O escopo deste processo de busca, incluindo:

  • Todos os arquivos de objeto gerados no tempo de compilação
  • Todas as bibliotecas (.lib) que são explicitamente ou implicitamente especificadas como dependências adicionais desse aplicativo de construção.

Este processo de pesquisa é chamado resolver.

D. Finalmente, por que símbolo externo não resolvido? Se o vinculador não puder encontrar a definição externa para um símbolo que não tenha nenhuma definição internamente, ele reportará um erro de símbolo externo não resolvido.

E. Causas possíveis do LNK2019 : Erro de símbolo externo não resolvido. Nós já sabemos que este erro é devido ao linker não conseguiu encontrar a definição de símbolos externos, as possíveis causas podem ser classificadas como:

  1. Definição existe

Por exemplo, se tivermos uma função chamada foo definida em a.cpp:

int foo()
{
    return 0;
}

Em b.cpp queremos chamar a função foo, então adicionamos

void foo();

para declarar a função foo () e chamá-la em outro corpo de função, digamos bar() :

void bar()
{
    foo();
}

Agora, quando você construir este código, você receberá um erro LNK2019 reclamando que foo é um símbolo não resolvido. Nesse caso, sabemos que o foo () tem sua definição em a.cpp, mas diferente daquele que estamos chamando (valor de retorno diferente). Este é o caso que a definição existe.

  1. Definição não existe

Se quisermos chamar algumas funções em uma biblioteca, mas a biblioteca de importação não será incluída na lista de dependências adicional (definida em: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency ) da configuração do seu projeto. Agora, o vinculador relatará um LNK2019, pois a definição não existe no escopo de pesquisa atual.


Arquivo .lib vinculado está associado a um .dll

Eu tive o mesmo problema. Digamos que eu tenha projetos MyProject e TestProject. Eu efetivamente vinculei o arquivo lib de MyProject ao TestProject. No entanto, esse arquivo lib foi produzido como a DLL para o MyProject foi criada. Além disso, eu não continha o código-fonte para todos os métodos no MyProject, mas apenas o acesso aos pontos de entrada da DLL.

Para resolver o problema, criei o MyProject como um LIB e vinculei o TestProject a esse arquivo .lib (copio e cole o arquivo .lib gerado na pasta TestProject). Eu posso então construir novamente MyProject como uma DLL. Ele está compilando desde que o lib ao qual o TestProject está vinculado contém código para todos os métodos em classes no MyProject.


O pacote do Visual Studio NuGet precisa ser atualizado para a nova versão do conjunto de ferramentas

Acabei de ter este problema ao tentar vincular a libpng ao Visual Studio 2013. O problema é que o arquivo de pacote tinha apenas bibliotecas para o Visual Studio 2010 e 2012.

A solução correta é esperar que o desenvolvedor libere um pacote atualizado e depois atualize, mas funcionou para mim invadindo uma configuração extra para o VS2013, apontando para os arquivos da biblioteca VS2012.

Eu editei o pacote (na pasta packages dentro do diretório da solução) encontrando o nome do pacote packagename\build\native\packagename.targets e dentro desse arquivo, copiando todas as seções v110 . Eu mudei o v110 para v120 nos campos de condição, sendo apenas muito cuidadoso para deixar os caminhos de nome de arquivo todos como v110 . Isso simplesmente permitiu que o Visual Studio 2013 se vinculasse às bibliotecas de 2012 e, nesse caso, funcionou.


Se tudo mais falhar, recompile.

Recentemente, consegui me livrar de um erro externo não resolvido no Visual Studio 2012 apenas recompilando o arquivo incorreto. Quando eu re-construído, o erro foi embora.

Isso geralmente acontece quando duas (ou mais) bibliotecas possuem uma dependência cíclica. A Biblioteca A tenta usar símbolos em B.lib e a biblioteca B tenta usar símbolos de A.lib. Nem existe para começar. Quando você tenta compilar A, a etapa do link falhará porque não pode encontrar B.lib. A.lib será gerado, mas não dll. Você então compila B, que terá sucesso e gerará B.lib. A recompilação de A agora funcionará porque o B.lib agora é encontrado.


Um wrapper em torno do GNU ld que não suporta scripts de linker

Alguns arquivos .so são, na verdade , scripts de vinculação GNU ld , por exemplo, o arquivo libtbb.so é um arquivo de texto ASCII com este conteúdo:

INPUT (libtbb.so.2)

Algumas construções mais complexas podem não suportar isso. Por exemplo, se você incluir -v nas opções do compilador, poderá ver que o mainwavnc wrapper mwdip descarta os arquivos de comando de script do vinculador na lista de saída detalhada das bibliotecas às quais é vinculado. Uma solução simples é substituir o comando de entrada do script vinculador arquivo com uma cópia do arquivo em vez disso (ou um symlink), por exemplo

cp libtbb.so.2 libtbb.so

Ou você pode substituir o argumento -l com o caminho completo do .so, por exemplo, em vez de -ltbbfazer/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2


Já que as pessoas parecem estar direcionadas para essa questão quando se trata de erros de linkers, vou acrescentar isso aqui.

Uma razão possível para os erros do linker com o GCC 5.2.0 é que uma nova ABI da biblioteca libstdc ++ agora é escolhida por padrão.

Se você obtiver erros de linker sobre referências indefinidas a símbolos que envolvem tipos no namespace std :: __ cxx11 ou na tag [abi: cxx11], provavelmente indica que você está tentando vincular arquivos de objeto que foram compilados com valores diferentes para o _GLIBCXX_USE_CXX11_ABI macro. Isso geralmente acontece ao vincular a uma biblioteca de terceiros que foi compilada com uma versão mais antiga do GCC. Se a biblioteca de terceiros não puder ser reconstruída com a nova ABI, você precisará recompilar seu código com a ABI antiga.

Então, se de repente você obter erros de linker ao trocar para um GCC após 5.1.0, isso seria uma coisa a se verificar.


Embora sejam perguntas bastante antigas com várias respostas aceitas, gostaria de compartilhar como resolver um erro obscuro de "referência indefinida para".

Versões diferentes de bibliotecas

Eu estava usando um alias para se referir a std::filesystem::path: sistema de arquivos está na biblioteca padrão desde C + + 17, mas meu programa precisava também compilar em C ++ 14, então decidi usar um alias de variável:

#if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and newer: <experimental/filesystem>)
using path_t = std::experimental::filesystem::path;
#elif (defined _GLIBCXX_FILESYSTEM) //not experimental (C++17 and newer: <filesystem>)
using path_t = std::filesystem::path;
#endif

Digamos que eu tenha três arquivos: main.cpp, file.h, file.cpp:

  • file.h # include's < experimental :: filestystem > e contém o código acima
  • file.cpp , a implementação de file.h, # include's " file.h "
  • MAIN.CPP # incluem de < filestystem > e " file.h "

Observe as diferentes bibliotecas usadas em main.cpp e file.h. Desde main.cpp # include'd " file.h " depois < filestystem >, a versão do sistema de arquivos usado houve o C ++ 17 um . Eu costumava compilar o programa com os seguintes comandos:

$ g++ -g -std=c++17 -c main.cpp-> compila main.cpp para main.o
$ g++ -g -std=c++17 -c file.cpp-> compila file.cpp e file.h para file.o
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs-> links main.o e file.o

Desta forma, qualquer função contida no arquivo.oe usada em main.o que requeriapath_t erros de "referência indefinida", porque main.o se referia std::filesystem::pathmas file.o to std::experimental::filesystem::path.

Resolução

Para corrigir isso eu só precisava mudar <experimental :: filestystem> em file.h para <filestystem> .





unresolved-external