web-dev-qa-db-ja.com

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

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

自分で編集/追加してください。

1342
Luchian Grigore

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

翻訳の構文規則の中での優先順位は、次のフェーズ[脚注]によって指定されます。

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

[footnote]実装はあたかもこれらの別々のフェーズが発生するかのように振る舞わなければなりませんが、実際には異なるフェーズが一緒に折り畳まれるかもしれません。

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

a.cppにシンボルaを定義したとします。さて、b.cppdeclaratedそのシンボルとそれを使った。リンクする前に、そのシンボルがsomewhereで定義されていると単純に仮定しますが、それはまだどこでも構いません。リンクフェーズは、シンボルを見つけて、それをb.cpp(実際にはそれを使用するオブジェクトまたはライブラリ)に正しくリンクする責任があります。

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

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

一般的なエラーメッセージは、error LNK2001error LNK1120 Microsoft Visual Studio error LNK2019、および _ gcc _ undefined reference tosymbolNameです。

コード:

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

Microsoft Visual Studio と同様のエラー

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

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

761
Luchian Grigore

クラスメンバー:

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

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

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

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

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

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

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()をpureとして宣言してください。

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

クラス定義内の整数型または列挙型のstaticconstデータメンバには、初期化子を指定できます。ただし、このメンバーの使用には、上記のとおり名前空間スコープの定義が必要です。 C++ 11では、すべてのstatic constデータメンバーのクラス内での初期化が許可されています。

163
Luchian Grigore

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

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

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 Directoriesにを追加する必要があります)libファイルで提供されているサードパーティ製のライブラリを使用する場合、通常、そうしないとエラーが発生します。

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

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

102
Luchian Grigore

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

典型的な変数宣言は

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)

ミスマッチの他の例としては、

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

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

94
Luchian Grigore

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

ライブラリが互いに依存している場合、ライブラリがリンクされている順序は重要です。一般に、ライブラリAがライブラリBに依存している場合、libAMUSTはリンカフラグの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

もう一度繰り返すと、DOという順番が重要です。

77
Svalorzen

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

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

注:私は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 --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

わかりました、私達はそれをハックします:)

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

68
Kastaneda

シンボルは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"
}
64
Luchian Grigore

それ以外のすべてが失敗した場合は、再コンパイルしてください。

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

これは通常、2つ(またはそれ以上)のライブラリーに循環依存関係があるときに起こります。ライブラリAはB.libのシンボルを使用しようとし、ライブラリBはA.libのシンボルを使用しようとします。どちらからも始めることができません。 Aをコンパイルしようとすると、B.libが見つからないため、リンク手順は失敗します。 A.libが生成されますが、DLLは生成されません。その後、Bをコンパイルします。これは成功し、B.libを生成します。 B.libが発見されたので、Aの再コンパイルは動作します。

61
sgryzko

モジュール/ 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
{
};
52
Luchian Grigore

これは、すべてのVC++プログラマーが何度も何度も見てきた最もわかりにくいエラーメッセージの1つです。最初に物事を明確にしましょう。

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

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

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

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

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

D.最後に、なぜ未解決の外部シンボルなのか? リンカが、内部定義を持たないシンボルの外部定義を見つけられない場合、Unresolved External Symbolエラーを報告します。

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をレポートするようになりました。

50
Nima Soroush

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

特殊化されていないテンプレートは、それらを使用するすべての翻訳単位から見えるように定義を持たなければなりません。つまり、テンプレートの定義を実装ファイルに分離することはできません。実装を分離する必要がある場合、通常の回避策はがテンプレートを宣言するヘッダーの最後に含める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の定義をヘッダーファイルまたはそれを使用する翻訳単位から見える場所に移動する必要があります。

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

さらなる説明と他の可能な解決策(明示的な具体化)については この質問と回答を参照してください

50
Luchian Grigore

未定義の[email protected]または類似の'unusual'main()エントリポイントの参照 (特に visual-studio の場合)。

実際のIDEでは正しいプロジェクトタイプを選択し損なっているかもしれません。 IDEはバインドしたい場合があります。 Windowsアプリケーションは、一般的に使用されているint main(int argc, char** argv);シグネチャの代わりに、そのようなエントリポイント関数(上記の欠落している参照で指定されている)に射影します。

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


これは現実世界問題からより詳細に扱われる case1case2 です。

37

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

34
Dula

マイクロソフトは、リンク時に正しいライブラリを参照するための#pragmaを提供しています。

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

ライブラリのディレクトリを含むライブラリパスに加えて、これはライブラリのフルネームでなければなりません。

33
Niall

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

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

正しい解決策は、開発者が更新されたパッケージをリリースしてからアップグレードすることを望みますが、VS2012ライブラリファイルを指定して、VS2013用の追加設定をハッキングすることによって私のために働きました。

私はpackagename\build\native\packagename.targetsとそのファイルの中を見つけて、すべてのv110セクションをコピーすることによって(ソリューションのディレクトリの中のpackagesフォルダーにある)パッケージを編集しました。 条件フィールドのみ v110v120に変更しました - /ファイル名のパスをすべてv110のままにするように注意してください。これにより、Visual Studio 2013から2012年のライブラリへのリンクが許可され、この場合は正常に機能しました。

31
Malvineous

C++で書かれた大きなプロジェクトがあり、そこには何千もの.cppファイルと何千もの.hファイルがあるとします。そして、このプロジェクトも10個の静的ライブラリに依存しているとしましょう。 Windows上にいて、プロジェクトをVisual Studio 20xxでビルドしているとしましょう。 Ctrl + F7 Visual Studioを押してソリューション全体のコンパイルを開始したとき(ソリューションにプロジェクトが1つだけあるとします)

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

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

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

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

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

観測

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

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

コンパイラ時間エラー: 

  • あなたがあなたのc ++プロジェクトの文法的に正しいことを書くことを確認してください。

リンカ時間エラー

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

コンパイラ/ IDEのバグ

私は最近この問題を抱えていました、そしてそれは結局のところ それはVisual Studio Express 2013のバグでした 。私はプロジェクトからソースファイルを削除し、バグを克服するためにそれを再追加しなければなりませんでした。

コンパイラ/ IDEのバグであると思われる場合に試す手順:

  • プロジェクトをクリーンアップします(IDEによってはこれを行うオプションがありますが、オブジェクトファイルを削除して手動で行うこともできます)。
  • 新しいプロジェクトを始めよう。オリジナルのものからすべてのソースコードをコピーする。
26
developerbmw

エラーの診断に役立つリンカを使用してください

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

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

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

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

23
Niall

Linked .libファイルは.dll に関連付けられています

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

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

23
octoback

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

GCC 5.2.0でのリンカエラーの考えられる原因の1つは、新しいlibstdc ++ライブラリABIがデフォルトで選択されていることです。

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

そのため、5.1.0以降にGCCに切り替えたときに突然リンカエラーが発生した場合は、これをチェックする必要があります。

18
Plankalkül

リンカスクリプトをサポートしないGNU ldのラッパー

いくつかの.soファイルは実際には GNU ldリンカスクリプト です。 libtbb.so fileは、次の内容を持つASCIIテキストファイルです。

INPUT (libtbb.so.2)

いくつかのより複雑なビルドはこれをサポートしないかもしれません。たとえば、コンパイラオプションに-vを含めると、 mainwin gcc wrapper mwdip はリンクするライブラリの詳細出力リストにあるリンカスクリプトコマンドファイルを破棄します。代わりに、リンカスクリプト入力コマンドファイルをファイルのコピー(またはシンボリックリンク)に置き換えます。

cp libtbb.so.2 libtbb.so

あるいは、-l引数を.soのフルパスに置き換えることもできます。 -ltbbの代わりに/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2を実行してください。

17
JDiMatteo

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

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

例は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

pkg-configを含む例2のバリエーション:

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

何を間違えていますか?

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

例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によって生成されたリンケージコマンドは、ディストリビューションで、リンケージ内のファイルをコマンドラインシーケンスで左から右に消費します。ファイルがsomethingを参照しており、その定義が含まれていないことが検出された場合、さらに右側のファイルの定義を検索します。最終的に定義が見つかった場合、参照は解決されます。最後に参照が解決されない場合、リンケージは失敗します。リンカーは逆方向に検索しません。

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

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

libmy_lib.aにはオブジェクトファイル、つまりmy_lib.oのみが存在し、my_lib.oには関数hwのみが定義されています。

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

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

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

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

リンカ追加されていませんeg1.oプログラムへ-lmy_libが表示されたとき。その時点では、eg1.oを見ていないためです。プログラムはまだhwへの参照を作成していません。作成する参照はすべてeg1.oにあるため、まだ参照を作成していませんat allです。

そのため、リンカはmy_lib.oをプログラムに追加せず、libmy_lib.aを使用しません。

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

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

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

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

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

それが当てはまる場合、リンカはnotlibzからチャンクをコピーしてプログラムに追加します。代わりに、次のようにプログラムのコードを修正するだけです。

  • 実行時に、システムプログラムローダーは、プログラムのコピーを読み込むたびに、libzのコピーをプログラムと同じプロセスに読み込んで実行します。

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

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

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

gcc -o eg2 -lz eg2.o

例1とまったく同じように、イベントの順序が間違っています。リンカが-lzを検出した時点で、プログラム内のあらゆるものへのno参照があります。 eg2.o、まだ見られていません。そのため、リンカはlibzを使用しないと判断します。 eg2.oに到達し、プログラムに追加し、zlibVersionへの未定義の参照がある場合、リンケージシーケンスは終了します。その参照は未解決であり、リンクは失敗します。

最後に、例2のpkg-configバリエーションには、明確な説明があります。シェル拡張後:

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

になる:

gcc -o eg2 -lz eg2.o

これも例2です。

例1では問題を再現できますが、例2では再現できません

リンケージ:

gcc -o eg2 -lz eg2.o

あなたのためにうまく動作します!

(または、Fedora 23ではそのリンケージは正常に機能しましたが、Ubuntu 16.04では失敗します)

これは、リンケージが機能するディストリビューションが、共有ライブラリをリンクするようにGCCツールチェーンを構成しないディストリビューションの1つであるためですas-needed

昔、Unixのようなシステムでは、異なるルールで静的ライブラリと共有ライブラリをリンクするのが普通でした。リンケージシーケンス内の静的ライブラリは、例1で説明したas-neededに基づいてリンクされましたが、共有ライブラリは無条件にリンクされました。

この動作は、共有ライブラリがプログラムで必要かどうかをリンカが熟考する必要がないため、リンク時に経済的です。共有ライブラリの場合は、リンクします。そして、ほとんどのリンケージのほとんどのライブラリは共有ライブラリです。しかし、欠点もあります:-

  • runtimeでは不経済です。共有ライブラリが必要でなくても、プログラムとともに共有ライブラリがロードされる可能性があるためです。

  • 静的ライブラリと共有ライブラリの異なるリンケージルールは、リンケージの-lfoo/some/where/libfoo.aに解決するのか/some/where/libfoo.soに解決するのかわからない未熟なプログラマーを混乱させる可能性があります。とにかく共有ライブラリと静的ライブラリの違いを理解してください。

このトレードオフが今日の分裂的な状況につながっています。一部のディストリビューションでは、共有ライブラリのGCCリンケージルールが変更され、すべてのライブラリにas-needed原則が適用されます。一部のディストリビューションは古い方法にこだわっています。

コンパイルとリンクを同時に行ってもこの問題が発生するのはなぜですか?

私がちょうどするなら:

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

確かに、gccは最初にeg1.cをコンパイルし、次に結果のオブジェクトファイルをlibmy_lib.aにリンクする必要があります。では、リンクを行っているときにオブジェクトファイルが必要であることをどのように認識できないのでしょうか。

単一のコマンドでコンパイルおよびリンクしても、リンクシーケンスの順序は変わらないためです。

上記のコマンドを実行すると、gccにより、コンパイル+リンケージが必要であることがわかります。そのため、舞台裏では、コンパイルコマンドを生成して実行し、次にリンケージコマンドを生成して実行します。これはyoが2つのコマンドを実行した場合と同じです。

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

doこれら2つのコマンドを実行した場合と同様に、リンケージは失敗します。失敗で気付く唯一の違いは、gccがコンパイル+リンクの場合にeg1.oを使用するように指示していないため、一時オブジェクトファイルを生成したことです。私たちは見る:

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

の代わりに:

eg1.o: In function `main':

こちらもご覧ください

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

相互依存ライブラリを間違った順序に並べることは、必要定義よりもリンケージで後から来るものの定義提供であるファイルを取得できる1つの方法にすぎません。ライブラリを参照するオブジェクトファイルの前にライブラリを置くことも、同じ間違いをする別の方法です。

17
Mike Kinghan

友好的なテンプレート...

フレンド演算子(または関数)を持つテンプレートタイプのコードスニペットを考えます。

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

operator<<は非テンプレート関数として宣言されています。 Tとともに使用されるすべての型Fooには、テンプレート化されていないoperator<<が必要です。たとえば、宣言された型Foo<int>がある場合、次のような演算子実装が必要です。

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

実装されていないため、リンカはそれを見つけられず、エラーになります。

これを修正するには、Foo型の前にテンプレート演算子を宣言してから、適切なインスタンス化である友人として宣言することができます。構文は少し厄介ですが、次のようになります。

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

上記のコードは、演算子の友情を対応するFooのインスタンス化に限定します。つまり、operator<< <int>インスタンス化は、Foo<int>のインスタンス化の非公開メンバーにアクセスすることに制限されます。

代替案があります。

  • 次のように、友情をテンプレートのすべてのインスタンス化に拡張することを許可します。

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
  • あるいは、operator<<の実装はクラス定義内でインラインで行うことができます。

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

、演算子(または関数)の宣言のみがクラスにある場合、 cppreference から、名前は「通常の」検索では使用できず、引数依存の検索でのみ使用できます。

クラスまたはクラステンプレートX内のフレンド宣言で最初に宣言された名前は、Xを囲む最も内側のネームスペースのメンバーになりますが、ネームスペーススコープでの一致する宣言が提供された...

cppreferenceC++ FAQ で、テンプレートの友達についてさらに読むことができます。

上記のテクニックを示すコードリスト


失敗したコードサンプルの補足として。 g ++はこれについて次のように警告します

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
Niall

一貫性のないUNICODE定義

Windows UNICODEビルドはTCHARなどで定義され、wchar_tなどで定義されます。UNICODEでビルドとして定義されていない場合、TCHARで定義されたcharなど。これらのUNICODEおよび_UNICODE定義は、すべての "T"文字列タイプ ;に影響します。 LPTSTRLPCTSTRおよびそのヘラジカ。

UNICODEが定義された1つのライブラリを構築し、UNICODEが定義されていないプロジェクトでリンクしようとすると、TCHARの定義に不一致があるため、リンカーエラーが発生します。 char vs. wchar_t

通常、エラーにはcharまたはwchar_t派生型の値を持つ関数が含まれますが、これらにはstd::basic_string<>なども含まれます。コード内の影響を受ける関数を参照すると、多くの場合、TCHARまたはstd::basic_string<TCHAR>などへの参照があります。これは、コードが元々UNICODEとMulti -バイト文字(または「狭い」)ビルド。

これを修正するには、UNICODE(および_UNICODE)の一貫した定義を使用して、必要なすべてのライブラリとプロジェクトをビルドします。

  1. これは、次のいずれかで実行できます。

    #define UNICODE
    #define _UNICODE
    
  2. またはプロジェクト設定で;

    プロジェクトのプロパティ>一般>プロジェクトのデフォルト>文字セット

  3. または、コマンドラインで。

    /DUNICODE /D_UNICODE
    

UNICODEを使用するつもりがない場合は、代替も適用できます。定義が設定されていないこと、および/またはプロジェクトで複数文字設定が使用され、一貫して適用されていることを確認してください。

「リリース」ビルドと「デバッグ」ビルドの間でも一貫性を保つことを忘れないでください。

15
Niall

インクルードパスが異なる場合

ヘッダファイルとそれに関連する共有ライブラリ(.libファイル)が同期しなくなると、リンカエラーが発生する可能性があります。説明させてください。 

リンカーはどのように機能しますか?リンカは、シグネチャを比較することによって、(ヘッダーで宣言された)関数宣言と(共有ライブラリ内の)定義を照合します。リンカが完全に一致する関数定義を見つけられない場合は、リンカエラーが発生する可能性があります。 

宣言と定義が一致しているように見えても、依然としてリンカーエラーが発生する可能性はありますか。はい!それらはソースコードで同じように見えるかもしれませんが、それは本当にコンパイラが見るものに依存します。基本的にあなたはこのような状況になる可能性があります。

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

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

両方の関数宣言がソースコード内で同じに見えても、コンパイラによって実際には異なっていることに注意してください。

あなたはそのような状況で人がどのように終わるのか尋ねるかもしれません? パスを含める もちろん!共有ライブラリをコンパイルするときにインクルードパスがheader1.hになり、あなた自身のプログラムでheader2.hを使用することになった場合、何が起こったのか疑問に思ってヘッダーをスクラッチしたままにしておくでしょう。

これが現実の世界でどのように発生する可能性があるかの例を以下に説明します。

例を用いたさらなる詳細

graphics.libmain.exeの2つのプロジェクトがあります。どちらのプロジェクトもcommon_math.hに依存しています。ライブラリが次の関数をエクスポートしたとします。

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

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

そして、あなたは先に進み、あなた自身のプロジェクトにライブラリを含めます。

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

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

ブーム!リンカエラーが発生し、それが失敗した理由がわかりません。その理由は、共通ライブラリが同じinclude common_math.hの異なるバージョンを使用しているからです(ここでは例として別のパスを含めることで明確にしましたが、必ずしも明確であるとは限りません。 ).

この例では、リンカがdraw()を見つけることができなかったと言っていることに注意してください。実際には、ライブラリによってエクスポートされていることが明らかです。何が悪かったのか疑問に思って頭をかいているのに何時間も費やすことができました。問題は、パラメータ型がわずかに異なるため、リンカは異なるシグネチャを認識することです。この例では、vec3は、コンパイラに関する限り、両方のプロジェクトで異なる型です。これは2つのわずかに異なるインクルードファイルから来ているために起こる可能性があります(インクルードファイルは2つの異なるバージョンのライブラリから来ている可能性があります)。

リンカのデバッグ

Visual Studioを使用している場合、DUMPBINはあなたの友達です。私は他のコンパイラが他の同様のツールを持っていると確信しています。

プロセスは次のようになります。

  1. リンカエラーで表示された奇妙なマングル名に注意してください。 (たとえば、@ graphics @ XYZを描きます)。
  2. ライブラリからエクスポートしたシンボルをテキストファイルにダンプします。
  3. 目的のエクスポートされたシンボルを検索して、マングル名が異なることに注意してください。
  4. 破損した名前が異なる結果になった理由に注意してください。ソースコードでは同じように見えますが、パラメータの種類が異なることがわかります。
  5. それらが異なる理由上記の例では、インクルードファイルが異なるために違います。

[1]プロジェクトとは、リンクされてライブラリまたは実行可能ファイルを生成する一連のソースファイルのことです。

編集1:理解しやすいように最初のセクションを書き直しました。何か他のものを修正する必要があるかどうか私に知らせるために下記にコメントしてください。ありがとうございます。

12
fafaro

クリーンで再構築

ビルドを「クリーン」にすると、以前のビルド、失敗したビルド、不完全なビルド、およびその他のビルドシステム関連のビルドの問題から取り残されている可能性がある「枯れ木」を除去できます。

一般的にIDEやbuildには何らかの "clean"機能が含まれますが、これは正しく設定されていないか(手動のmakefileなどで)、失敗することがあります).

「クリーン」が完了したら、「クリーン」が成功し、生成されたすべての中間ファイル(自動メークファイルなど)が正常に削除されたことを確認します。

このプロセスは最終的な手段と見なすことができますが、多くの場合、最初の良いステップです;特に、エラーに関連するコードが最近(ローカルまたはソースリポジトリから)追加された場合。

12
Niall

const変数の宣言/定義に "extern"がありません(C++のみ)

Cから来た人々にとって、C++のグローバルなconstvariablesが内部的な(あるいは静的な)リンケージを持っているのは驚きかもしれません。 Cでは、すべてのグローバル変数が暗黙的にexternであるため(つまり、staticキーワードがない場合)、これは当てはまりませんでした。

例: 

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

正しい方法は、ヘッダーファイルを使用してそれをfile2.cpp および file1.cppに含めることです。

extern const int test;
extern int test2;

あるいは、明示的なconstを使用してfile1.cppでextern変数を宣言できます。

7
Andreas H.

これは複数の回答が受け入れられているかなり古い質問ですが、obscure"undefined reference to"エラーの解決方法を共有したいと思います。

ライブラリの異なるバージョン

std::filesystem::pathを参照するエイリアスを使用していました:C++ 17以降、ファイルシステムは標準ライブラリにありますが、私のプログラムはC++ 14でコンパイルする必要があるため、変数エイリアスを使用することにしました:

#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

Main.cpp、file.h、file.cppの3つのファイルがあるとします。

  • file.h #includeの<experimental :: filesystem>および上記のコードが含まれています
  • file.cpp、file.hの実装、#includeの「file.h
  • main.cpp #includeの<filesystem>および "file.h"

Main.cppとfile.hで使用されるdifferent librariesに注意してください。 main.cppは<filesystem>の後に「file.h」を#includeしたため、そこで使用されたファイルシステムのバージョンはthe C + +17 one。次のコマンドを使用してプログラムをコンパイルしていました。

$ g++ -g -std=c++17 -c main.cpp-> main.cppをmain.oにコンパイルします
$ g++ -g -std=c++17 -c file.cpp-> file.cppとfile.hをfile.oにコンパイルします
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs-> main.oとfile.oをリンクします

このようにany functionはfile.oに含まれ、required path_tmain.oのために「未定義の参照」エラーを出したmain.oで使用されます。std::filesystem::pathを参照しますが、file.ostd::experimental::filesystem::pathを参照します。

解決

これを修正するには、file.hの<experimental :: filesystem>を<filesystem>に変更する必要がありました。

6
Stypox

共有ライブラリに対してリンクするときは、使用されているシンボルが隠されていないことを確認してください。

Gccのデフォルトの動作は、すべてのシンボルが見えることです。ただし、翻訳単位がオプション-fvisibility=hiddenで作成されている場合、結果として得られる共有オブジェクトでは、__attribute__ ((visibility ("default")))でマークされた関数/シンボルのみが外部にあります。

あなたが探しているシンボルが外部のものであるかどうか確認することができます:

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

隠れた/ローカルなシンボルは小文字のシンボル型のnmで示されます、例えばコードセクションのための `Tの代わりにt

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

(C++が使用されている場合は)nmをオプション-Cと共に使用して名前をデマングルすることもできます。

Windows-dllと同様に、パブリック関数にdefineを付けることができます。例えば、DLL_PUBLICは次のように定義されます。

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

DLL_PUBLIC int my_public_function(){
  ...
}

これは、Windows/MSVC-versionにほぼ対応します。

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

より多くの 可視性に関する情報 がgcc wikiにあります。


翻訳単位が-fvisibility=hiddenでコンパイルされるとき、結果のシンボルはまだ外部リンケージを持ち(nmで大文字のシンボルタイプで示されます)、オブジェクトファイルが静的ライブラリの一部になった場合、問題なく外部リンケージに使用できます。リンクは、オブジェクトファイルが共有ライブラリにリンクされている場合にのみローカルになります。

オブジェクトファイル内のどのシンボルが隠されているかを調べるには、次のコマンドを実行します。

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
2
ead

さまざまなアーキテクチャ

次のようなメッセージが表示されるかもしれません。

library machine type 'x64' conflicts with target machine type 'X86'

その場合、それは利用可能なシンボルがあなたがコンパイルしているものとは異なるアーキテクチャ用であることを意味します。

Visual Studioでは、これは間違った「プラットフォーム」が原因であり、適切なものを選択するか、適切なバージョンのライブラリをインストールする必要があります。

Linuxでは、それは間違ったライブラリフォルダが原因であるかもしれません(例えばlib64の代わりにlibを使う)。

MacOSでは、両方のアーキテクチャを同じファイルで出荷するという選択肢があります。リンクは両方のバージョンがあることを期待しているかもしれませんが、1つだけです。ライブラリが拾われる間違ったlib/lib64フォルダの問題でもあるかもしれません。

0

エラーが発生するのは、リンカが宣言ファイル内の定義に対する定義、つまりヘッダファイルまたは宣言ファイルからの定義を見つけられなかった場合です。実装が見つからないときに発生します。 

コンパイル後、リンカはライブラリから、または仮想関数用またはテンプレート定義用の実装を検索しようとします。見つからない場合は、これらのエラーはリンカのアーキテクチャに従って発生します。

0
Karthik Krish
  • G ++ 44(Red Hat 4.4.7-8)で、コードをそのままコンパイルすることができます。

UbuntuにはDebianパッチが適用されていますgcc-4.4/ g ++ - 4.4 :このコードにはダメです(他のns2コード/パッチも同様)。

Ubuntu:mpolsr_umolsr-v1_ns235.patchhttps://drive.google.com/file/d/0B7S...ew?usp=sharing (mpolsrコードで2017年作成、変更なし)

tar xvf ns-allinone-2.35_gcc5.tar.gz

https://drive.google.com/file/d/0B7S...ew?usp=sharing

cd ns-allinone-2.35/
patch -p0 < mpolsr_umolsr-v1_ns235.patch  // umolsr version v1.0 is used
./install
              // Stops with MPOLSR.cc
cd ns-2.35/
              // Edit the Makefile line 37 to CPP = g++34 , and run make
make
              // When 'make' stops with an mdart/* error, change to CPP = g++-4.4
make

gcc34 Ubuntu https://drive.google.com/file/d/0B7S255p3kFXNRTkzQnRSNXZ6UVU/view?usp=sharing

g ++ 34 Ubuntu https://drive.google.com/file/d/0B7S255p3kFXNV3J3bnVoWGNWdG8/view?usp=sharing

0
Knud Larsen

関数またはクラスメソッドはinline指定子を使ってソースファイルで定義されます。

例:-

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

gum(同様に、foo::bar)がその定義においてinlineであると指定した場合、コンパイラはgumをインライン化します(選択した場合)。

  • gumの固有の定義を出さないので、
  • リンカがgumの定義を参照できるようなシンボルを生成しません。代わりに
  • gumへのすべての呼び出しをgumのコンパイル済み本体のインラインコピーに置き換えます。

その結果、ソースファイルgum.cppgumをインラインで定義すると、gumへのすべての呼び出しがインライン化され、シンボルが定義されていないオブジェクトファイルgum.oにコンパイルされます。 gumを参照できます。 gum.oを他のオブジェクトファイルと一緒にプログラムにリンクするとき。外部シンボルgumへの参照を作成するmain.o では、リンカはそれらの参照を解決できません。そのため、リンケージは失敗します。

コンパイル:

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

リンク:

$ 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

gumを呼び出すことができるすべてのソースファイルでコンパイラがその定義を参照できる場合にのみ、inlinegumとして定義できます。これは、そのインライン定義がheaderファイルに存在する必要があることを意味します。あなたがコンパイルするすべてのソースファイルには、include _ gumを呼び出すことができます。 2つのうちの1つをしてください:

定義をインライン化しないでください

ソースファイル定義からinline指定子を削除します。

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

それを再構築します。

$ g++ -c  main.cpp foo.cpp gum.cpp
[email protected]:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
[email protected]:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const

成功。

または正しくインライン -

ヘッダーファイル内のインライン定義

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

foo.cppgum.cppは必要ありません。

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