未定義の参照/未解決の外部シンボルエラーとは何ですか?どのように修正しますか?

Sep 25 2012

未定義の参照/未解決の外部シンボルエラーとは何ですか?一般的な原因とその修正/防止方法は何ですか?

自由に編集/追加してください。

回答

880 LuchianGrigore Sep 25 2012 at 05:27

C ++プログラムのコンパイルは、2.2で 指定されているように、いくつかのステップで行われます(参照用のKeith Thompsonのクレジット)

翻訳の構文規則間の優先順位は、次のフェーズで指定されます[脚注を参照]

  1. 物理ソースファイルの文字は、必要に応じて、実装で定義された方法で、基本的なソース文字セット(行末インジケーターに改行文字を導入)にマップされます。[をちょきちょきと切る]
  2. バックスラッシュ文字(\)の直後に改行文字が続く各インスタンスが削除され、物理ソース行が接続されて論理ソース行が形成されます。[をちょきちょきと切る]
  3. ソースファイルは、前処理トークン(2.5)と空白文字のシーケンス(コメントを含む)に分解されます。[をちょきちょきと切る]
  4. 前処理ディレクティブが実行され、マクロ呼び出しが展開され、_Pragma単項演算子式が実行されます。[をちょきちょきと切る]
  5. 文字リテラルまたは文字列リテラルの各ソース文字セットメンバー、および文字リテラルまたは非生文字列リテラルの各エスケープシーケンスとユニバーサル文字名は、実行文字セットの対応するメンバーに変換されます。[をちょきちょきと切る]
  6. 隣接する文字列リテラルトークンは連結されます。
  7. トークンを区切る空白文字は重要ではなくなりました。各前処理トークンはトークンに変換されます。(2.7)。結果のトークンは、構文的および意味的に分析され、翻訳単位として翻訳されます。[をちょきちょきと切る]
  8. 翻訳された翻訳単位とインスタンス化単位は、次のように組み合わされます。[SNIP]
  9. すべての外部エンティティ参照が解決されます。ライブラリコンポーネントは、現在の翻訳で定義されていないエンティティへの外部参照を満たすためにリンクされています。このようなトランスレータ出力はすべて、実行環境での実行に必要な情報を含むプログラムイメージに収集されます。(私の強調)

[脚注]実装は、これらの別々のフェーズが発生するかのように動作する必要がありますが、実際には、異なるフェーズが一緒に折りたたまれている場合があります。

指定されたエラーは、コンパイルのこの最後の段階で発生します。これは、最も一般的にはリンクと呼ばれます。これは基本的に、一連の実装ファイルをオブジェクトファイルまたはライブラリにコンパイルし、それらを連携させたいことを意味します。

でシンボルを定義したとaa.cppます。さて、そのシンボルをb.cpp 宣言して使用しました。リンクする前に、そのシンボルがどこかで定義されていると単純に想定しますが、それはまだどこを気にしません。リンクフェーズでは、シンボルを見つけて正しくリンクしますb.cpp(実際には、シンボルを使用するオブジェクトまたはライブラリにリンクします)。

Microsoft Visual Studioを使用している場合は、プロジェクトが.libファイルを生成することがわかります。これらには、エクスポートされたシンボルのテーブルとインポートされたシンボルのテーブルが含まれています。インポートされたシンボルは、リンク先のライブラリに対して解決され、エクスポートされたシンボルは、それを使用するライブラリ.lib(存在する場合)に提供されます。

他のコンパイラ/プラットフォームにも同様のメカニズムが存在します。

一般的なエラーメッセージがあるerror LNK2001error LNK1120error LNK2019のためのMicrosoftのVisual Studioundefined reference to にSymbolNameのためのGCC

コード:

struct X
{
   virtual void foo();
};
struct Y : X
{
   void foo() {}
};
struct A
{
   virtual ~A() = 0;
};
struct B: A
{
   virtual ~B(){}
};
extern int x;
void foo();
int main()
{
   x = 0;
   foo();
   Y y;
   B b;
}

GCCで次のエラーが生成されます:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

およびMicrosoftVisual Studioでの同様のエラー:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

一般的な原因は次のとおりです。

185 LuchianGrigore Sep 25 2012 at 06:38

クラスのメンバー:

純粋なvirtualデストラクタには実装が必要です。

デストラクタを純粋に宣言するには、(通常の関数とは異なり)それを定義する必要があります。

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

これは、オブジェクトが暗黙的に破棄されたときに基本クラスのデストラクタが呼び出されるために発生するため、定義が必要です。

virtual メソッドは、実装するか、純粋として定義する必要があります。

これは、virtual定義のない非メソッドに似ていますが、純粋な宣言によってダミーのvtableが生成され、関数を使用せずにリンカーエラーが発生する可能性があるという理由が追加されています。

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

これが機能するX::foo()ためには、純粋であると宣言します。

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

virtualクラスメンバ

一部のメンバーは、明示的に使用されていない場合でも定義する必要があります。

struct A
{ 
    ~A();
};

次の場合、エラーが発生します。

A a;      //destructor undefined

実装は、クラス定義自体でインラインにすることができます。

struct A
{ 
    ~A() {}
};

または外部:

A::~A() {}

実装がクラス定義の外にあるがヘッダーにある場合、inline複数の定義を防ぐためにメソッドをマークする必要があります。

使用する場合は、使用するすべてのメンバーメソッドを定義する必要があります。

よくある間違いは、名前の修飾を忘れることです。

struct A
{
   void foo();
};

void foo() {}

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

定義は次のようになります

void A::foo() {}

staticデータメンバーは、クラスの外部の単一の変換単位で定義する必要があります。

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

初期化子はstatic const、クラス定義内の整数型または列挙型のデータメンバーに提供できます。ただし、このメンバーのodr-useには、上記の名前空間スコープ定義が必要です。C ++ 11では、すべてのstatic constデータメンバーのクラス内での初期化が可能です。

121 LuchianGrigore Sep 25 2012 at 06:37

適切なライブラリ/オブジェクトファイルへのリンクまたは実装ファイルのコンパイルの失敗

通常、各翻訳ユニットは、その翻訳ユニットで定義されたシンボルの定義を含むオブジェクトファイルを生成します。これらのシンボルを使用するには、これらのオブジェクトファイルに対してリンクする必要があります。

gccの下で、コマンドラインで一緒にリンクされるすべてのオブジェクトファイルを指定するか、実装ファイルを一緒にコンパイルします。

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

これlibraryNameは、プラットフォーム固有の追加を除いた、ライブラリの単なる名前です。したがって、たとえばLinuxでは、ライブラリファイルは通常呼び出されますが、libfoo.so書き込むだけ-lfooです。Windowsでは、同じファイルが呼び出されるfoo.lib場合がありますが、同じ引数を使用します。を使用して、これらのファイルを見つけることができるディレクトリを追加する必要がある場合があります-L‹directory›-lまたはの後にスペースを入れないでください-L

以下の場合はXCode:>ドラッグとプロジェクトフォルダに実際のライブラリの参照をドロップ- -ライブラリ検索パスを追加>ヘッダ検索パスのユーザーを追加します。

下でMSVS、プロジェクトに追加されたファイルは、自動的にそのオブジェクトファイルは、一緒にリンクしているとlib、ファイルは(一般的な使い方で)生成されます。別のプロジェクトでシンボルを使用するにはlib、プロジェクト設定にファイルを含める必要があります。これは、プロジェクトプロパティのリンカーセクションで行われInput -> Additional Dependenciesます。(libファイルへのパスはに追加する必要がありますLinker -> General -> Additional Library Directorieslibファイルとともに提供されるサードパーティライブラリを使用する場合、そうしないと通常エラーが発生します。

ファイルをコンパイルに追加するのを忘れた場合もあります。その場合、オブジェクトファイルは生成されません。gccでは、ファイルをコマンドラインに追加します。でMSVSは、プロジェクトにファイルを追加し、それは(ファイルはいえ、手動で、個別にビルドから除外することができます)、それを自動的にコンパイルようになります。

Windowsプログラミングでは、必要なライブラリをリンクしなかったことを示す兆候は、未解決のシンボルの名前がで始まること__imp_です。ドキュメントで関数の名前を調べると、使用する必要のあるライブラリが示されているはずです。たとえば、MSDNは、「ライブラリ」というセクションの各関数の下部にあるボックスに情報を配置します。

105 LuchianGrigore Sep 25 2012 at 06:38

宣言されていますが、変数または関数を定義していません。

典型的な変数宣言は

extern int x;

これは単なる宣言であるため、単一の定義が必要です。対応する定義は次のとおりです。

int x;

たとえば、次の場合はエラーが発生します。

extern int x;
int main()
{
    x = 0;
}
//int x; // uncomment this line for successful definition

同様の注意が関数にも当てはまります。関数を定義せずに宣言すると、エラーが発生します。

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition

実装する関数が宣言した関数と完全に一致するように注意してください。たとえば、cv-qualifiersが一致していない可能性があります。

void foo(int& x);
int main()
{
   int x;
   foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
                          //for void foo(int& x)
                          

不一致の他の例には、

  • ある名前空間で宣言され、別の名前空間で定義された関数/変数。
  • クラスメンバーとして宣言され、グローバルとして定義された関数/変数(またはその逆)。
  • 関数の戻り値の型、パラメーターの番号と型、および呼び出し規約がすべて完全に一致しているわけではありません。

コンパイラからのエラーメッセージは、宣言されたが定義されていない変数または関数の完全な宣言を提供することがよくあります。提供した定義とよく比較してください。すべての詳細が一致することを確認してください。

89 Svalorzen Jul 10 2014 at 18:46

相互依存するリンクライブラリが指定される順序が間違っています。

ライブラリが相互に依存している場合、ライブラリがリンクされる順序は重要です。ライブラリがあれば一般的には、Aライブラリに依存Bし、libA しなければならないの前に表示されるlibBリンカのフラグに。

例えば:

// 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;
};

ライブラリを作成します。

$ 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

コンパイル:

$ 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

だから、もう一度繰り返すため、注文はDOESの問題を!

76 Kastaneda Oct 07 2014 at 17:09

「未定義の参照/未解決の外部シンボル」とは何ですか

「未定義の参照/未解決の外部シンボル」とは何かを説明しようと思います。

注:私はg ++とLinuxを使用しており、すべての例はそれのためのものです

たとえば、いくつかのコードがあります

// src1.cpp
void print();

static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;

int main()
{
    print();
    return 0;
}

そして

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
//extern int local_var_name;

void print ()
{
    // printf("%d%d\n", global_var_name, local_var_name);
    printf("%d\n", global_var_name);
}

オブジェクトファイルを作成する

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

アセンブラフェーズの後、エクスポートするシンボルを含むオブジェクトファイルが作成されます。シンボルを見てください

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

関係ないので、出力からいくつかの行を拒否しました

したがって、エクスポートする次の記号が表示されます。

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cppは何もエクスポートせず、そのシンボルも見ていません。

オブジェクトファイルをリンクする

$ g++ src1.o src2.o -o prog

そしてそれを実行します

$ ./prog
123

リンカーは、エクスポートされたシンボルを確認してリンクします。ここで、src2.cppの行のコメントを解除しようとします。

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

オブジェクトファイルを再構築します

$ g++ -c src2.cpp -o src2.o

OK(エラーなし)。オブジェクトファイルのみをビルドするため、リンクはまだ完了していません。リンクしてみてください

$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status

これは、local_var_nameが静的であるため、つまり他のモジュールでは表示されないために発生しました。今より深く。翻訳フェーズの出力を取得する

$ g++ -S src1.cpp -o src1.s

// src1.s
look src1.s

    .file   "src1.cpp"
    .local  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    .globl  global_var_name
    .data
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; assembler code, not interesting for us
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

したがって、local_var_nameのラベルがないことがわかりました。そのため、リンカーはそれを検出しませんでした。しかし、私たちはハッカーです:)そしてそれを修正することができます。テキストエディタでsrc1.sを開き、変更します

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

つまり、以下のようにする必要があります

    .file   "src1.cpp"
    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789
    .globl  global_var_name
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; ...

local_var_nameの可視性を変更し、その値を456789に設定しました。そこからオブジェクトファイルを作成してみてください。

$ g++ -c src1.s -o src2.o

わかりました、readelf出力(シンボル)を参照してください

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

現在、local_var_nameにはBind GLOBALがあります(以前はLOCALでした)

リンク

$ g++ src1.o src2.o -o prog

そしてそれを実行します

$ ./prog 
123456789

わかりました、ハックします:)

そのため、結果として、リンカがオブジェクトファイルでグローバルシンボルを見つけることができない場合、「未定義の参照/未解決の外部シンボルエラー」が発生します。

73 LuchianGrigore Sep 25 2012 at 06:39

シンボルはCプログラムで定義され、C ++コードで使用されました。

関数(または変数)void foo()がCプログラムで定義されており、C ++プログラムで使用しようとしています。

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

C ++リンカーは、名前がマングルされることを想定しているため、関数を次のように宣言する必要があります。

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

同様に、Cプログラムで定義される代わりに、関数(または変数)void foo()はC ++で定義されましたが、Cリンケージを使用しました。

extern "C" void foo();

そして、C ++リンケージを使用してC ++プログラムで使用しようとします。

ライブラリ全体がヘッダーファイルに含まれている(そしてCコードとしてコンパイルされている)場合。インクルードは次のようにする必要があります。

extern "C" {
    #include "cheader.h"
}
70 sgryzko Dec 04 2013 at 01:11

他のすべてが失敗した場合は、再コンパイルします。

最近、問題のあるファイルを再コンパイルするだけで、Visual Studio2012の未解決の外部エラーを取り除くことができました。再構築すると、エラーはなくなりました。

これは通常、2つ(またはそれ以上)のライブラリに循環依存がある場合に発生します。ライブラリAはB.libのシンボルを使用しようとし、ライブラリBはA.libのシンボルを使用しようとします。どちらも最初から存在しません。Aをコンパイルしようとすると、B.libが見つからないため、リンクステップは失敗します。A.libは生成されますが、dllは生成されません。次に、Bをコンパイルします。これは成功し、B.libを生成します。B.libが見つかったため、Aの再コンパイルが機能するようになりました。

60 LuchianGrigore Sep 25 2012 at 06:39

モジュール/ dll間でメソッド/クラスを誤ってインポート/エクスポートします(コンパイラ固有)。

MSVSは使用してエクスポートおよびインポートするシンボルを指定する必要があります__declspec(dllexport)__declspec(dllimport)

この二重の機能は通常、マクロを使用して取得されます。

#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif

マクロTHIS_MODULEは、関数をエクスポートするモジュールでのみ定義されます。そのように、宣言:

DLLIMPEXP void foo();

に展開します

__declspec(dllexport) void foo();

現在のモジュールにはその定義が含まれているため、関数をエクスポートするようコンパイラーに指示します。別のモジュールに宣言を含めると、次のように展開されます。

__declspec(dllimport) void foo();

そして、定義がリンク先のライブラリの1つにあることをコンパイラに通知します(1も参照)。

同様に、クラスをインポート/エクスポートできます。

class DLLIMPEXP X
{
};
59 LuchianGrigore Sep 25 2012 at 06:38

テンプレートの実装は表示されません。

特殊化されていないテンプレートでは、それらを使用するすべての翻訳ユニットに定義が表示される必要があります。つまり、テンプレートの定義を実装ファイルに分離することはできません。実装を分離する必要がある場合、通常の回避策はimpl、テンプレートを宣言するヘッダーの最後に含めるファイルを用意することです。一般的な状況は次のとおりです。

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

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

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

これを修正するには、の定義をX::fooヘッダーファイルまたはそれを使用する翻訳ユニットから見える場所に移動する必要があります。

特殊化されたテンプレートは実装ファイルに実装でき、実装を表示する必要はありませんが、特殊化は事前に宣言する必要があります。

詳細な説明と別の可能な解決策(明示的なインスタンス化)については、この質問と回答を参照してください。

58 NimaSoroush Apr 13 2015 at 23:42

これは、すべてのVC ++プログラマーが何度も目にする最も紛らわしいエラーメッセージの1つです。まず、物事を明確にしましょう。

A.シンボルとは何ですか? 要するに、シンボルは名前です。変数名、関数名、クラス名、typedef名、またはC ++言語に属する名前と記号以外の名前にすることができます。これは、依存関係ライブラリ(別のユーザー定義)によってユーザー定義または導入されます。

B.外部とは何ですか? VC ++では、すべてのソースファイル(.cpp、.cなど)が変換単位と見なされ、コンパイラーは一度に1つの単位をコンパイルし、現在の変換単位に対して1つのオブジェクトファイル(.obj)を生成します。(このソースファイルに含まれるすべてのヘッダーファイルは前処理され、この翻訳ユニットの一部と見なされることに注意してください)翻訳ユニット内のすべては内部と見なされ、その他はすべて外部と見なされます。C ++では、次のようなキーワードを使用して外部シンボルを参照することができextern__declspec (dllimport)というように。

C.「解決」とは何ですか? 解決はリンク時間の用語です。リンク時、リンカは、オブジェクトファイル内のすべてのシンボルの外部定義を見つけようとしますが、その定義は内部で見つけることができません。この検索プロセスの範囲は次のとおりです。

  • コンパイル時に生成されたすべてのオブジェクトファイル
  • このビルドアプリケーションの追加の依存関係として明示的または暗黙的に指定されているすべてのライブラリ(.lib)。

この検索プロセスは解決と呼ばれます。

D.最後に、なぜ未解決の外部シンボルなのですか? リンカは、内部に定義がないシンボルの外部定義を見つけることができない場合、未解決の外部シンボルエラーを報告します。

E. LNK2019の考えられる原因:未解決の外部シンボルエラー。このエラーは、リンカーが外部シンボルの定義を見つけられなかったことが原因であることがすでにわかっています。考えられる原因は次のように並べ替えることができます。

  1. 定義が存在します

たとえば、a.cppで定義されたfooという関数がある場合:

int foo()
{
    return 0;
}

b.cppで関数fooを呼び出したいので、追加します

void foo();

関数foo()を宣言し、それを別の関数本体で呼び出すには、次のように言いますbar()

void bar()
{
    foo();
}

このコードをビルドすると、fooが未解決のシンボルであるというLNK2019エラーが発生します。この場合、foo()の定義はa.cppにありますが、呼び出しているものとは異なります(異なる戻り値)。これは、定義が存在する場合です。

  1. 定義が存在しません

ライブラリ内のいくつかの関数を呼び出したいが、インポートライブラリがProject | Properties | Configuration Properties | Linker | Input | Additional Dependencyプロジェクト設定の追加の依存関係リスト(から設定:)に追加されていない場合。現在の検索範囲に定義が存在しないため、リンカーはLNK2019を報告します。

43 πάνταῥεῖ Mar 04 2014 at 06:52

未定義の参照WinMain@16または類似の「異常な」 main()エントリポイント参照(特にvisual-studioの場合)。

実際のIDEで適切なプロジェクトタイプを選択できなかった可能性があります。IDEは、一般的に使用されるint main(int argc, char** argv);署名の代わりに、たとえばWindowsアプリケーションプロジェクトをそのようなエントリポイント関数(上記の欠落しているリファレンスで指定されている)にバインドしたい場合があります。

IDEがプレーンコンソールプロジェクトをサポートしている場合は、Windowsアプリケーションプロジェクトではなく、このプロジェクトタイプを選択することをお勧めします。


これは、実際の問題からより詳細に処理されたcase1case2です。

37 Dula Jun 19 2014 at 00:06

また、サードパーティのライブラリを使用している場合は、正しい32/64ビットバイナリがあることを確認してください

35 Niall Sep 09 2014 at 19:09

Microsoftは、#pragmaリンク時に正しいライブラリを参照することを提案しています。

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

ライブラリのディレクトリを含むライブラリパスに加えて、これはライブラリのフルネームである必要があります。

35 Malvineous Jan 17 2015 at 09:24

Visual Studio NuGetパッケージは、新しいツールセットバージョン用に更新する必要があります

libpngをVisualStudio 2013にリンクしようとすると、この問題が発生しました。問題は、パッケージファイルにVisual Studio2010および2012のライブラリしかないことです。

正しい解決策は、開発者が更新されたパッケージをリリースしてからアップグレードすることを期待することですが、VS2012ライブラリファイルをポイントして、VS2013の追加設定をハッキングすることでうまくいきました。

パッケージ(packagesソリューションのディレクトリ内のフォルダー内)を見つけpackagename\build\native\packagename.targetsてそのファイル内で編集し、すべてのv110セクションをコピーしました。ファイル名のパスをすべて。のままにするように注意して、条件フィールドのv110をに変更しv120ましv110。これにより、Visual Studio 2013が2012年のライブラリにリンクできるようになり、この場合は機能しました。

35 Noname Aug 11 2015 at 22:33

数千の.cppファイルと数千の.hファイルを持つc ++で記述された大きなプロジェクトがあるとします。そして、プロジェクトが10個の静的ライブラリにも依存しているとしましょう。Windowsを使用していて、Visual Studio20xxでプロジェクトをビルドするとします。Ctrl + F7 Visual Studioを押して、ソリューション全体のコンパイルを開始すると(ソリューションにプロジェクトが1つしかない場合)

コンパイルの意味は何ですか?

  • Visual Studioはファイル.vcxprojを検索し、拡張子が.cppの各ファイルのコンパイルを開始します。コンパイルの順序は定義されていないため、main.cppファイルが最初にコンパイルされると想定してはなりません。
  • .cppファイルがファイル.cppで定義されているかどうかわからないシンボルを見つけるために、追加の.hファイルに依存している場合
  • コンパイラが1つのシンボルを見つけることができなかった1つの.cppファイルが存在する場合、コンパイラの時間エラーにより、「シンボルxが見つかりませんでした」というメッセージが表示されます。
  • 拡張子が.cppのファイルごとに、オブジェクトファイル.oが生成され、Visual Studioは、リンカーで処理する必要のあるすべてのオブジェクトファイルを含むProjectName.Cpp.Clean.txtという名前のファイルに出力を書き込みます。

コンパイルの2番目のステップは、リンカーによって実行されます。リンカーは、すべてのオブジェクトファイルをマージし、最終的に出力(実行可能ファイルまたはライブラリの場合があります)をビルドする必要があります。

プロジェクトをリンクする手順

  • すべてのオブジェクトファイルを解析し、ヘッダーでのみ宣言された定義を見つけます(例:前の回答で述べたクラスの1つのメソッドのコード、またはクラス内のメンバーである静的変数の初期化のイベント)
  • オブジェクトファイルで1つのシンボルが見つからなかった場合は、追加ライブラリでも検索されます。プロジェクトに新しいライブラリを追加するには、[構成プロパティ] -> [ VC ++ディレクトリ] -> [ライブラリディレクトリ]を選択し、ここでライブラリと構成プロパティを検索するための追加フォルダを指定しました->リンカ->ライブラリの名前を指定するための入力。-リンカーが1つの.cppに書き込んだシンボルを見つけられなかった場合、リンカーの時間エラーが発生しますerror LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

観察

  1. リンカーが1つのシンボルを見つけると、他のライブラリでそのシンボルを検索しません。
  2. ライブラリをリンクする順序は重要です。
  3. リンカーが1つの静的ライブラリで外部シンボルを見つけた場合、プロジェクトの出力にそのシンボルを含めます。ただし、ライブラリが共有(動的)の場合、出力にコード(シンボル)を含めませんが、 ランタイムクラッシュが発生する可能性があります。起こる

この種のエラーを解決する方法

コンパイラ時間エラー:

  • C ++プロジェクトを構文的に正しく記述していることを確認してください。

リンカー時間エラー

  • ヘッダーファイルで宣言するすべてのシンボルを定義します
  • #pragma onceコンパイルされている現在の.cppに既に含まれている場合に、コンパイラが1つのヘッダーを含めないようにするために使用します
  • 外部ライブラリに、ヘッダーファイルで定義した他のシンボルと競合する可能性のあるシンボルが含まれていないことを確認してください
  • テンプレートを使用して、各テンプレート関数の定義をヘッダーファイルにインクルードして、コンパイラーがインスタンス化に適切なコードを生成できるようにする場合。
29 developerbmw Jan 03 2015 at 05:06

コンパイラ/ IDEのバグ

最近この問題が発生しましたが、Visual Studio Express2013のバグであることが判明しました。バグを克服するために、プロジェクトからソースファイルを削除し、再度追加する必要がありました。

コンパイラ/ IDEのバグである可能性があると思われる場合の手順:

  • プロジェクトをクリーンアップします(一部のIDEにはこれを行うオプションがありますが、オブジェクトファイルを削除して手動で行うこともできます)
  • 元のプロジェクトからすべてのソースコードをコピーして、新しいプロジェクトを開始してみてください。
27 Niall Jul 27 2015 at 17:20

リンカを使用してエラーの診断に役立てる

最新のリンカーのほとんどには、さまざまな程度で出力される詳細オプションが含まれています。

  • リンク呼び出し(コマンドライン)、
  • リンクステージに含まれるライブラリに関するデータ、
  • ライブラリの場所、
  • 使用される検索パス。

gccおよびclangの場合。通常、-v -Wl,--verboseまたは-v -Wl,-vをコマンドラインに追加します。詳細については、こちらをご覧ください。

MSVCの場合、/VERBOSE(特に/VERBOSE:LIB)がリンクコマンドラインに追加されます。

26 kiriloff Apr 04 2014 at 22:02

リンクされた.libファイルは.dllに関連付けられています

私も同じ問題を抱えていました。MyProjectプロジェクトとTestProjectプロジェクトがあるとします。MyProjectのlibファイルをTestProjectに効果的にリンクしました。ただし、このlibファイルは、MyProjectのDLLが作成されたときに作成されました。また、MyProjectのすべてのメソッドのソースコードを含めたわけではなく、DLLのエントリポイントへのアクセスのみを含めました。

この問題を解決するために、MyProjectをLIBとしてビルドし、TestProjectをこの.libファイルにリンクしました(生成された.libファイルをTestProjectフォルダーにコピーアンドペーストします)。その後、MyProjectをDLLとして再ビルドできます。TestProjectがリンクされているlibには、MyProjectのクラスのすべてのメソッドのコードが含まれているため、コンパイル中です。

25 Plankalkül Sep 10 2015 at 18:03

リンカーエラーに関しては、人々はこの質問に向けられているように思われるので、ここにこれを追加します。

GCC 5.2.0でのリンカーエラーの考えられる理由の1つは、新しいlibstdc ++ライブラリABIがデフォルトで選択されるようになったことです。

std :: __ cxx11名前空間またはタグ[abi:cxx11]の型を含むシンボルへの未定義の参照に関するリンカーエラーが発生した場合は、_GLIBCXX_USE_CXX11_ABIの異なる値でコンパイルされたオブジェクトファイルをリンクしようとしていることを示している可能性があります。大きい。これは通常、古いバージョンのGCCでコンパイルされたサードパーティのライブラリにリンクするときに発生します。サードパーティライブラリを新しいABIで再構築できない場合は、古いABIでコードを再コンパイルする必要があります。

したがって、5.1.0以降のGCCに切り替えるときに突然リンカーエラーが発生した場合は、これを確認する必要があります。

21 MikeKinghan Apr 09 2017 at 17:34

リンケージは、ライブラリを参照するオブジェクトファイルの前にライブラリを消費します

  • プログラムをコンパイルしてGCCツールチェーンにリンクしようとしています。
  • リンケージは、必要なすべてのライブラリとライブラリ検索パスを指定します
  • libfoo依存する場合libbar、リンケージは正しくlibfoo前に配置されlibbarます。
  • リンケージがundefined reference to 何かエラーで失敗します
  • しかし、すべての未定義のものは、あなたが持っているヘッダーファイルで宣言されて #includeおり、実際にはあなたがリンクしているライブラリで定義されています。

例はCにあります。C++でも同様です。

自分で作成した静的ライブラリを含む最小限の例

my_lib.c

#include "my_lib.h"
#include <stdio.h>

void hw(void)
{
    puts("Hello World");
}

my_lib.h

#ifndef MY_LIB_H
#define MT_LIB_H

extern void hw(void);

#endif

eg1.c

#include <my_lib.h>

int main()
{
    hw();
    return 0;
}

静的ライブラリを構築します。

$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o

プログラムをコンパイルします。

$ gcc -I. -c -o eg1.o eg1.c

あなたはそれをリンクしようとしてlibmy_lib.a失敗します:

$ gcc -o eg1 -L. -lmy_lib eg1.o 
eg1.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status

次のように、1つのステップでコンパイルしてリンクした場合も同じ結果になります。

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
/tmp/ccQk1tvs.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status

共有システムライブラリである圧縮ライブラリを含む最小限の例 libz

eg2.c

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%s\n",zlibVersion());
    return 0;
}

プログラムをコンパイルします。

$ gcc -c -o eg2.o eg2.c

プログラムをリンクしlibzて失敗してみてください。

$ gcc -o eg2 -lz eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status

一度にコンパイルしてリンクする場合も同じです。

$ gcc -o eg2 -I. -lz eg2.c
/tmp/ccxCiGn7.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status

そして、以下を含む例2のバリエーションpkg-config

$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'

何が悪いの?

プログラムを作成するためにリンクするオブジェクトファイルとライブラリのシーケンスでは、ライブラリを参照するオブジェクトファイルの前にライブラリを配置します。ライブラリを参照するオブジェクトファイルのにライブラリを配置する必要があります。

例1を正しくリンクします。

$ gcc -o eg1 eg1.o -L. -lmy_lib

成功:

$ ./eg1 
Hello World

例2を正しくリンクします。

$ gcc -o eg2 eg2.o -lz

成功:

$ ./eg2 
1.2.8

例2のpkg-configバリエーションを正しくリンクします。

$ gcc -o eg2 eg2.o $(pkg-config --libs zlib) 
$ ./eg2
1.2.8

説明

これ以降、読み取りはオプションです

デフォルトでは、ディストリビューションでGCCによって生成されたリンケージコマンドは、コマンドラインシーケンスで左から右にリンケージ内のファイルを消費します。ファイルが何か を参照していて、その定義が含まれていないことがわかった場合、toはさらに右側のファイルで定義を検索します。最終的に定義が見つかった場合、参照は解決されます。最後に未解決のままの参照がある場合、リンケージは失敗します。リンカは逆方向に検索しません。

まず、例1、静的ライブラリを使用my_lib.a

静的ライブラリは、オブジェクトファイルのインデックス付きアーカイブです。リンカが-lmy_libリンケージシーケンスを見つけて、これが静的ライブラリを参照している./libmy_lib.aことを理解すると、プログラムがのオブジェクトファイルを必要としているかどうかを知りたいと考えていますlibmy_lib.a

にはオブジェクトファイルlibmy_lib.a、つまりmy_lib.o、だけがあり、で定義されているものは1つだけmy_lib.o、つまり関数hwです。

リンカはmy_lib.o、プログラムが参照していることをすでに認識している場合にのみ、プログラムが必要であると判断します。hwこれは、プログラムにすでに追加されている1つ以上のオブジェクトファイルで、すでに追加されているオブジェクトファイルにの定義hw

それが当てはまる場合、リンカはmy_lib.oライブラリからのコピーを抽出し、それをプログラムに追加します。次に、プログラムにの定義が含まれているためhw、への参照hw解決されます。

次のようにプログラムをリンクしようとすると:

$ gcc -o eg1 -L. -lmy_lib eg1.o

リンカ 、を見つけたときにeg1.o プログラム追加ていません-lmy_lib。その時点では見ていませんのでeg1.o。あなたのプログラムはまだへの参照がありませんhw:それはまだすべての参照がありませんすべてでは、すべての参照を可能にしているのでeg1.o

したがって、リンカはmy_lib.oプログラムに追加されず、それ以上の用途はありませんlibmy_lib.a

次に、を見つけてeg1.oプログラムに追加します。リンケージシーケンスのオブジェクトファイルは常にプログラムに追加されます。現在、プログラムはを参照しhwhw;の定義は含まれていません。しかし、欠落している定義を提供する可能性のあるリンケージシーケンスには何も残っていません。への参照hw未解決になり、リンケージは失敗します。

次に、例2、共有ライブラリを使用libz

共有ライブラリは、オブジェクトファイルなどのアーカイブではありません。これは、関数を持たず、代わりにそれが定義する他の複数のシンボルを公開するプログラムに非常に似ているmainため、他のプログラムは実行時にそれらを使用できます。

多くのLinuxディストリビューションは本日、言語ドライバ(ように彼らのGCCツールチェーンを構成しgccg++gfortran(システムリンカに指示など)ldリンクに)上の共有ライブラリなど、必要に応じて基礎。あなたはそれらのディストリビューションの1つを持っています。

これは、リンカが-lzリンケージシーケンスを見つけて、これが共有ライブラリを/usr/lib/x86_64-linux-gnu/libz.so参照していることを認識した場合(たとえば)、プログラムに追加された、まだ定義されていない参照に、次のような定義があるかどうかを知りたいことを意味します。によってエクスポートされたlibz

それが当てはまる場合、リンカはチャンクをコピーしてプログラムに追加しませんlibz。代わりに、それはあなたのプログラムのコードをただ医者にするでしょう:-

  • 実行時に、システムプログラムローダーはlibz、プログラムのコピーをロードするたびに、プログラムのコピーをプログラムと同じプロセスにロードして実行します。

  • 実行時に、プログラムがで定義されているものを参照するときはいつでも libz、その参照はlibz同じプロセスでのコピーによってエクスポートされた定義を使用します。

プログラムは、によってエクスポートされた定義を持つ1つのものlibz、つまり、でzlibVersion1回だけ参照される関数を参照したいと考えていますeg2.c。リンカがその参照をプログラムに追加し、によってエクスポートされた定義を見つけたlibz場合、その参照は解決されます

But when you try to link the program like:

gcc -o eg2 -lz eg2.o

the order of events is wrong in just the same way as with example 1. At the point when the linker finds -lz, there are no references to anything in the program: they are all in eg2.o, which has not yet been seen. So the linker decides it has no use for libz. When it reaches eg2.o, adds it to the program, and then has undefined reference to zlibVersion, the linkage sequence is finished; that reference is unresolved, and the linkage fails.

Lastly, the pkg-config variation of example 2 has a now obvious explanation. After shell-expansion:

gcc -o eg2 $(pkg-config --libs zlib) eg2.o

becomes:

gcc -o eg2 -lz eg2.o

which is just example 2 again.

I can reproduce the problem in example 1, but not in example 2

The linkage:

gcc -o eg2 -lz eg2.o

works just fine for you!

(Or: That linkage worked fine for you on, say, Fedora 23, but fails on Ubuntu 16.04)

That's because the distro on which the linkage works is one of the ones that does not configure its GCC toolchain to link shared libraries as-needed.

Back in the day, it was normal for unix-like systems to link static and shared libraries by different rules. Static libraries in a linkage sequence were linked on the as-needed basis explained in example 1, but shared libraries were linked unconditionally.

This behaviour is economical at linktime because the linker doesn't have to ponder whether a shared library is needed by the program: if it's a shared library, link it. And most libraries in most linkages are shared libraries. But there are disadvantages too:-

  • It is uneconomical at runtime, because it can cause shared libraries to be loaded along with a program even if doesn't need them.

  • The different linkage rules for static and shared libraries can be confusing to inexpert programmers, who may not know whether -lfoo in their linkage is going to resolve to /some/where/libfoo.a or to /some/where/libfoo.so, and might not understand the difference between shared and static libraries anyway.

This trade-off has led to the schismatic situation today. Some distros have changed their GCC linkage rules for shared libraries so that the as-needed principle applies for all libraries. Some distros have stuck with the old way.

Why do I still get this problem even if I compile-and-link at the same time?

If I just do:

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c

surely gcc has to compile eg1.c first, and then link the resulting object file with libmy_lib.a. So how can it not know that object file is needed when it's doing the linking?

Because compiling and linking with a single command does not change the order of the linkage sequence.

When you run the command above, gcc figures out that you want compilation + linkage. So behind the scenes, it generates a compilation command, and runs it, then generates a linkage command, and runs it, as if you had run the two commands:

$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o

So the linkage fails just as it does if you do run those two commands. The only difference you notice in the failure is that gcc has generated a temporary object file in the compile + link case, because you're not telling it to use eg1.o. We see:

/tmp/ccQk1tvs.o: In function `main'

instead of:

eg1.o: In function `main':

See also

The order in which interdependent linked libraries are specified is wrong

Putting interdependent libraries in the wrong order is just one way in which you can get files that need definitions of things coming later in the linkage than the files that provide the definitions. Putting libraries before the object files that refer to them is another way of making the same mistake.

20 JDiMatteo Mar 30 2015 at 23:03

A wrapper around GNU ld that doesn't support linker scripts

Some .so files are actually GNU ld linker scripts, e.g. libtbb.so file is an ASCII text file with this contents:

INPUT (libtbb.so.2)

Some more complex builds may not support this. For example, if you include -v in the compiler options, you can see that the mainwin gcc wrapper mwdip discards linker script command files in the verbose output list of libraries to link in. A simple work around is to replace the linker script input command file with a copy of the file instead (or a symlink), e.g.

cp libtbb.so.2 libtbb.so

Or you could replace the -l argument with the full path of the .so, e.g. instead of -ltbb do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2

18 Niall Mar 09 2016 at 19:08

Befriending templates...

Given the code snippet of a template type with a friend operator (or function);

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

The operator<< is being declared as a non-template function. For every type T used with Foo, there needs to be a non-templated operator<<. For example, if there is a type Foo<int> declared, then there must be an operator implementation as follows;

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

Since it is not implemented, the linker fails to find it and results in the error.

To correct this, you can declare a template operator before the Foo type and then declare as a friend, the appropriate instantiation. The syntax is a little awkward, but is looks as follows;

// 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
}

The above code limits the friendship of the operator to the corresponding instantiation of Foo, i.e. the operator<< <int> instantiation is limited to access the private members of the instantiation of Foo<int>.

Alternatives include;

  • Allowing the friendship to extend to all instantiations of the templates, as follows;

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
  • Or, the implementation for the operator<< can be done inline inside the class definition;

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

Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided...

There is further reading on template friends at cppreference and the C++ FAQ.

Code listing showing the techniques above.


As a side note to the failing code sample; g++ warns about this as follows

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)

16 fafaro Mar 03 2017 at 13:57

When your include paths are different

Linker errors can happen when a header file and its associated shared library (.lib file) go out of sync. Let me explain.

How do linkers work? The linker matches a function declaration (declared in the header) with its definition (in the shared library) by comparing their signatures. You can get a linker error if the linker doesn't find a function definition that matches perfectly.

Is it possible to still get a linker error even though the declaration and the definition seem to match? Yes! They might look the same in source code, but it really depends on what the compiler sees. Essentially you could end up with a situation like this:

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

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

Note how even though both the function declarations look identical in source code, but they are really different according to the compiler.

You might ask how one ends up in a situation like that? Include paths of course! If when compiling the shared library, the include path leads to header1.h and you end up using header2.h in your own program, you'll be left scratching your header wondering what happened (pun intended).

An example of how this can happen in the real world is explained below.

Further elaboration with an example

I have two projects: graphics.lib and main.exe. Both projects depend on common_math.h. Suppose the library exports the following function:

// graphics.lib    
#include "common_math.h" 
   
void draw(vec3 p) { ... } // vec3 comes from common_math.h

And then you go ahead and include the library in your own project.

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

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

Boom! You get a linker error and you have no idea why it's failing. The reason is that the common library uses different versions of the same include common_math.h (I have made it obvious here in the example by including a different path, but it might not always be so obvious. Maybe the include path is different in the compiler settings).

Note in this example, the linker would tell you it couldn't find draw(), when in reality you know it obviously is being exported by the library. You could spend hours scratching your head wondering what went wrong. The thing is, the linker sees a different signature because the parameter types are slightly different. In the example, vec3 is a different type in both projects as far as the compiler is concerned. This could happen because they come from two slightly different include files (maybe the include files come from two different versions of the library).

Debugging the linker

DUMPBIN is your friend, if you are using Visual Studio. I'm sure other compilers have other similar tools.

The process goes like this:

  1. Note the weird mangled name given in the linker error. (eg. draw@graphics@XYZ).
  2. Dump the exported symbols from the library into a text file.
  3. Search for the exported symbol of interest, and notice that the mangled name is different.
  4. Pay attention to why the mangled names ended up different. You would be able to see that the parameter types are different, even though they look the same in the source code.
  5. Reason why they are different. In the example given above, they are different because of different include files.

[1] By project I mean a set of source files that are linked together to produce either a library or an executable.

EDIT 1: Rewrote first section to be easier to understand. Please comment below to let me know if something else needs to be fixed. Thanks!

15 Niall Apr 07 2016 at 18:53

Inconsistent UNICODE definitions

A Windows UNICODE build is built with TCHAR etc. being defined as wchar_t etc. When not building with UNICODE defined as build with TCHAR defined as char etc. These UNICODE and _UNICODE defines affect all the "T" string types; LPTSTR, LPCTSTR and their elk.

Building one library with UNICODE defined and attempting to link it in a project where UNICODE is not defined will result in linker errors since there will be a mismatch in the definition of TCHAR; char vs. wchar_t.

The error usually includes a function a value with a char or wchar_t derived type, these could include std::basic_string<> etc. as well. When browsing through the affected function in the code, there will often be a reference to TCHAR or std::basic_string<TCHAR> etc. This is a tell-tale sign that the code was originally intended for both a UNICODE and a Multi-Byte Character (or "narrow") build.

To correct this, build all the required libraries and projects with a consistent definition of UNICODE (and _UNICODE).

  1. This can be done with either;

    #define UNICODE
    #define _UNICODE
    
  2. Or in the project settings;

    Project Properties > General > Project Defaults > Character Set

  3. Or on the command line;

    /DUNICODE /D_UNICODE
    

The alternative is applicable as well, if UNICODE is not intended to be used, make sure the defines are not set, and/or the multi-character setting is used in the projects and consistently applied.

Do not forget to be consistent between the "Release" and "Debug" builds as well.

14 Niall Feb 24 2016 at 17:40

Clean and rebuild

A "clean" of the build can remove the "dead wood" that may be left lying around from previous builds, failed builds, incomplete builds and other build system related build issues.

In general the IDE or build will include some form of "clean" function, but this may not be correctly configured (e.g. in a manual makefile) or may fail (e.g. the intermediate or resultant binaries are read-only).

Once the "clean" has completed, verify that the "clean" has succeeded and all the generated intermediate file (e.g. an automated makefile) have been successfully removed.

This process can be seen as a final resort, but is often a good first step; especially if the code related to the error has recently been added (either locally or from the source repository).

10 AndreasH. Aug 03 2017 at 15:01

Missing "extern" in const variable declarations/definitions (C++ only)

For people coming from C it might be a surprise that in C++ global constvariables have internal (or static) linkage. In C this was not the case, as all global variables are implicitly extern (i.e. when the static keyword is missing).

Example:

// 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
}

correct would be to use a header file and include it in file2.cpp and file1.cpp

extern const int test;
extern int test2;

Alternatively one could declare the const variable in file1.cpp with explicit extern

8 Stypox Aug 27 2018 at 23:43

Even though this is a pretty old questions with multiple accepted answers, I'd like to share how to resolve an obscure "undefined reference to" error.

Different versions of libraries

I was using an alias to refer to std::filesystem::path: filesystem is in the standard library since C++17 but my program needed to also compile in C++14 so I decided to use a variable alias:

#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

Let's say I have three files: main.cpp, file.h, file.cpp:

  • file.h #include's <experimental::filesystem> and contains the code above
  • file.cpp, the implementation of file.h, #include's "file.h"
  • main.cpp #include's <filesystem> and "file.h"

Note the different libraries used in main.cpp and file.h. Since main.cpp #include'd "file.h" after <filesystem>, the version of filesystem used there was the C++17 one. I used to compile the program with the following commands:

$ g++ -g -std=c++17 -c main.cpp -> compiles main.cpp to main.o
$ g++ -g -std=c++17 -c file.cpp -> compiles file.cpp and file.h to file.o
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs -> links main.o and file.o

This way any function contained in file.o and used in main.o that required path_t gave "undefined reference" errors because main.o referred to std::filesystem::path but file.o to std::experimental::filesystem::path.

Resolution

To fix this I just needed to change <experimental::filesystem> in file.h to <filesystem>.

5 ead Sep 07 2018 at 13:22

When linking against shared libraries, make sure that the used symbols are not hidden.

The default behavior of gcc is that all symbols are visible. However, when the translation units are built with option -fvisibility=hidden, only functions/symbols marked with __attribute__ ((visibility ("default"))) are external in the resulting shared object.

You can check whether the symbols your are looking for are external by invoking:

# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL 

the hidden/local symbols are shown by nm with lowercase symbol type, for example t instead of `T for code-section:

nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL

You can also use nm with the option -C to demangle the names (if C++ was used).

Similar to Windows-dlls, one would mark public functions with a define, for example DLL_PUBLIC defined as:

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

DLL_PUBLIC int my_public_function(){
  ...
}

Which roughly corresponds to Windows'/MSVC-version:

#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

More information about visibility can be found on the gcc wiki.


When a translation unit is compiled with -fvisibility=hidden the resulting symbols have still external linkage (shown with upper case symbol type by nm) and can be used for external linkage without problem if the object files become part of a static libraries. The linkage becomes local only when the object files are linked into a shared library.

To find which symbols in an object file are hidden run:

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
3 MikeKinghan Jan 22 2019 at 02:44

Functions or class-methods are defined in source files with the inline specifier.

An example:-

main.cpp

#include "gum.h"
#include "foo.h"

int main()
{
    gum();
    foo f;
    f.bar();
    return 0;
}

foo.h (1)

#pragma once

struct foo {
    void bar() const;
};

gum.h (1)

#pragma once

extern void gum();

foo.cpp (1)

#include "foo.h"
#include <iostream>

inline /* <- wrong! */ void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp (1)

#include "gum.h"
#include <iostream>

inline /* <- wrong! */ void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

If you specify that gum (similarly, foo::bar) is inline at its definition then the compiler will inline gum (if it chooses to), by:-

  • not emitting any unique definition of gum, and therefore
  • not emitting any symbol by which the linker can refer to the definition of gum, and instead
  • replacing all calls to gum with inline copies of the compiled body of gum.

As a result, if you define gum inline in a source file gum.cpp, it is compiled to an object file gum.o in which all calls to gum are inlined and no symbol is defined by which the linker can refer to gum. When you link gum.o into a program together with another object file, e.g. main.o that make references to an external symbol gum, the linker cannot resolve those references. So the linkage fails:

Compile:

g++ -c  main.cpp foo.cpp gum.cpp

Link:

$ g++ -o prog main.o foo.o gum.o
main.o: In function `main':
main.cpp:(.text+0x18): undefined reference to `gum()'
main.cpp:(.text+0x24): undefined reference to `foo::bar() const'
collect2: error: ld returned 1 exit status

You can only define gum as inline if the compiler can see its definition in every source file in which gum may be called. That means its inline definition needs to exist in a header file that you include in every source file you compile in which gum may be called. Do one of two things:

Either don't inline the definitions

Remove the inline specifier from the source file definition:

foo.cpp (2)

#include "foo.h"
#include <iostream>

void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp (2)

#include "gum.h"
#include <iostream>

void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

Rebuild with that:

$ g++ -c  main.cpp foo.cpp gum.cpp
imk@imk-Inspiron-7559:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
imk@imk-Inspiron-7559:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const

Success.

Or inline correctly

Inline definitions in header files:

foo.h (2)

#pragma once
#include <iostream>

struct foo {
    void bar() const  { // In-class definition is implicitly inline
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};
// Alternatively...
#if 0
struct foo {
    void bar() const;
};
inline void foo::bar() const  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}
#endif

gum.h (2)

#pragma once
#include <iostream>

inline void gum() {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

Now we don't need foo.cpp or gum.cpp:

$ g++ -c main.cpp
$ g++ -o prog main.o
$ ./prog
void gum()
void foo::bar() const