未定義の参照/未解決の外部シンボルエラーとは何ですか?一般的な原因は何ですか?それらを修正/防止する方法は?
自由に編集/追加してください。
未定義の参照/未解決の外部シンボルエラーとは何ですか?一般的な原因は何ですか?それらを修正/防止する方法は?
自由に編集/追加してください。
回答:
C ++プログラムのコンパイルは、2.2で 指定されているように、いくつかのステップで行われます(参照用にキーストンプソンへのクレジット)。
翻訳の構文規則間の優先順位は、次のフェーズで指定されます[脚注を参照]。
- 物理ソースファイルの文字は、必要に応じて、実装定義の方法で、基本的なソース文字セット(行末インジケーターの改行文字を導入)にマップされます。[をちょきちょきと切る]
- バックスラッシュ文字(\)の直後に改行文字が続くインスタンスはそれぞれ削除され、物理ソース行が結合されて論理ソース行が形成されます。[をちょきちょきと切る]
- ソースファイルは、前処理トークン(2.5)と一連の空白文字(コメントを含む)に分解されます。[をちょきちょきと切る]
- 前処理ディレクティブが実行され、マクロ呼び出しが展開され、_Pragma単項演算子式が実行されます。[をちょきちょきと切る]
- 文字リテラルまたは文字列リテラルの各ソース文字セットメンバー、および文字リテラルまたは非raw文字列リテラルの各エスケープシーケンスとユニバーサル文字名は、実行文字セットの対応するメンバーに変換されます。[をちょきちょきと切る]
- 隣接する文字列リテラルトークンが連結されます。
- トークンを区切る空白文字は重要ではなくなりました。各前処理トークンはトークンに変換されます。(2.7)。結果のトークンは、構文的および意味的に分析され、翻訳単位として翻訳されます。[をちょきちょきと切る]
- 翻訳された翻訳単位とインスタンス化単位は次のように組み合わされます:[SNIP]
- すべての外部エンティティ参照が解決されます。ライブラリコンポーネントは、現在の翻訳で定義されていないエンティティへの外部参照を満たすためにリンクされます。このようなトランスレータの出力はすべて、その実行環境での実行に必要な情報を含むプログラムイメージに収集されます。(強調鉱山)
[脚注]実際には異なるフェーズが一緒に折りたたまれることもありますが、実装はこれらの個別のフェーズが発生するかのように動作する必要があります。
指定されたエラーは、コンパイルのこの最後の段階で発生し、最も一般的にはリンクと呼ばれます。基本的には、一連の実装ファイルをオブジェクトファイルまたはライブラリにコンパイルし、それらを連携させる必要があることを意味します。
でシンボルを定義したとa
しa.cpp
ます。今、そのシンボルをb.cpp
宣言して使用しました。リンクする前に、単にそのシンボルがどこかで定義されていると仮定しますが、まだどこでもかまいません。リンクフェーズでは、シンボルを見つけて、正しくb.cpp
(実際には、それを使用するオブジェクトまたはライブラリに)リンクします。
Microsoft Visual Studioを使用している場合は、プロジェクトが.lib
ファイルを生成することがわかります。これらには、エクスポートされたシンボルのテーブルとインポートされたシンボルのテーブルが含まれています。インポートされたシンボルは、リンク先のライブラリに対して解決され、エクスポートされたシンボルは、それを使用するライブラリ.lib
(存在する場合)に提供されます。
他のコンパイラ/プラットフォームにも同様のメカニズムが存在します。
一般的なエラーメッセージがあるerror LNK2001
、error LNK1120
、error LNK2019
のためのMicrosoftのVisual Studioとundefined 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
およびMicrosoft Visual 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
一般的な原因は次のとおりです。
#pragma
(Microsoft Visual Studio)を使用しているときに.lib拡張子を誤って入力したり含めないUNICODE
定義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の使用には、上記の名前空間スコープの定義が必要です。C ++ 11では、すべてのstatic const
データメンバーのクラス内の初期化が可能です。
通常、各変換単位は、その変換単位で定義されたシンボルの定義を含むオブジェクトファイルを生成します。これらのシンボルを使用するには、それらのオブジェクトファイルに対してリンクする必要があります。
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は「ライブラリ」というセクションの各関数の下部にあるボックスに情報を配置します。
gcc main.c
ではなく、一般的な間違いを明示的にカバーできればよいでしょうgcc main.c other.c
。
典型的な変数宣言は
extern int x;
これは単なる宣言なので、1つの定義が必要です。対応する定義は次のようになります。
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)
不一致の他の例には、
コンパイラからのエラーメッセージは、宣言されたが定義されていない変数または関数の完全な宣言を提供することがよくあります。指定した定義とよく比較してください。すべての詳細が一致していることを確認してください。
ライブラリが相互に依存している場合、ライブラリがリンクされる順序は重要です。ライブラリがあれば一般的には、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の問題を!
「未定義の参照/未解決の外部シンボル」とは
「未定義の参照/未解決の外部シンボル」とは何かを説明しようと思います。
注:私は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
はい、ハックします:)
したがって、結果として、リンカーがオブジェクトファイルでグローバルシンボルを見つけられない場合、「未定義の参照/未解決の外部シンボルエラー」が発生します。
関数(または変数)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"
}
#ifdef __cplusplus [\n] extern"C" { [\n] #endif
と#ifdef __cplusplus [\n] } [\n] #endif
([\n]
本当のキャリッジ・リターンであることが、私はコメントでこれを適切に書き込むことはできません)。
extern "C" { #include <myCppHeader.h> }
。
他のすべてが失敗した場合は、再コンパイルします。
最近、問題のあるファイルを再コンパイルするだけで、Visual Studio 2012で未解決の外部エラーを取り除くことができました。私が再構築したとき、エラーはなくなりました。
これは通常、2つ(またはそれ以上)のライブラリに循環依存関係がある場合に発生します。ライブラリAはB.libのシンボルを使用しようとし、ライブラリBはA.libのシンボルを使用しようとします。最初はどちらも存在しません。Aをコンパイルしようとすると、B.libが見つからないため、リンク手順が失敗します。A.libは生成されますが、dllは生成されません。次に、Bをコンパイルします。これは成功し、B.libを生成します。B.libが見つかったので、Aの再コンパイルは機能します。
MSVSでは、およびを使用して__declspec(dllexport)
、エクスポートおよびインポートするシンボルを指定する必要があります__declspec(dllimport)
。
この2つの機能は通常、マクロを使用して取得されます。
#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
{
};
visibility
とWindowsの.def
ファイルについて言及する必要があります。これらもシンボル名と存在に影響を与えるためです。
.def
古くからファイルを使用していません。回答を追加するか、これを編集してください。
これは、すべてのVC ++プログラマーが何度も何度も目にした最もわかりにくいエラーメッセージの1つです。最初に物事を明確にしましょう。
A.シンボルとは? つまり、シンボルは名前です。変数名、関数名、クラス名、typedef名、またはC ++言語に属する名前と記号を除くすべての名前を使用できます。これは、依存ライブラリ(別のユーザー定義)によってユーザー定義または導入されます。
B.外部とは何ですか?
VC ++では、すべてのソースファイル(.cpp、.cなど)が翻訳単位と見なされ、コンパイラは一度に1つの単位をコンパイルし、現在の翻訳単位に対して1つのオブジェクトファイル(.obj)を生成します。(このソースファイルに含まれるすべてのヘッダーファイルは前処理され、この翻訳単位の一部と見なされることに注意してください)翻訳単位内のすべてが内部と見なされ、それ以外はすべて外部と見なされます。C ++では、などのキーワードを使用して外部シンボルを参照できますextern
。__declspec (dllimport)
というように。
C.「解決」とは何ですか? 解決はリンク時の用語です。リンク時、リンカーは、内部で定義を見つけることができないオブジェクトファイル内のすべてのシンボルの外部定義を見つけようとします。この検索プロセスの範囲:
この検索プロセスは、解決と呼ばれます。
D.最後に、なぜ未解決の外部シンボルですか? リンカは、内部に定義がないシンボルの外部定義を見つけることができない場合、未解決の外部シンボルエラーを報告します。
E. LNK2019の考えられる原因:未解決の外部シンボルエラー。このエラーは、リンカが外部シンボルの定義を見つけられなかったことが原因であることがすでにわかっています。考えられる原因は次のように分類できます。
たとえば、a.cppで定義されたfooという関数がある場合:
int foo()
{
return 0;
}
b.cppでは、関数fooを呼び出したいので、
void foo();
関数foo()を宣言し、それを別の関数本体で呼び出すには、次のようにしますbar()
。
void bar()
{
foo();
}
このコードをビルドすると、fooが未解決のシンボルであるというLNK2019エラーが表示されます。この場合、foo()の定義はa.cppにありますが、呼び出しているものとは異なります(異なる戻り値)。これは定義が存在する場合です。
ライブラリの一部の関数を呼び出したいが、インポートライブラリがProject | Properties | Configuration Properties | Linker | Input | Additional Dependency
プロジェクト設定の追加の依存関係リスト(から設定)に追加されていない場合。現在、定義は現在の検索範囲に存在しないため、リンカーはLNK2019を報告します。
特殊化されていないテンプレートの定義は、それらを使用するすべての翻訳単位に表示される必要があります。つまり、テンプレートの定義を実装ファイルに分離することはできません。実装を分離する必要がある場合、通常の回避策は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
ヘッダーファイルまたはそれを使用する翻訳単位から見える場所にます。
特殊化されたテンプレートは実装ファイルに実装でき、実装は可視である必要はありませんが、特殊化は事前に宣言する必要があります。
詳細な説明と考えられる別の解決策(明示的なインスタンス化)については、この質問と回答を参照してください。
未定義の参照WinMain@16
または同様の「異常な」 main()
エントリポイント参照(特にビジュアルスタジオ)。
実際のIDEで適切なプロジェクトタイプを選択し忘れている可能性があります。IDEは、一般的に使用されるint main(int argc, char** argv);
シグネチャの代わりに、たとえばWindowsアプリケーションプロジェクトをそのようなエントリポイント関数(上記の欠落しているリファレンスで指定されている)にバインドする場合があります。
IDEがプレーンコンソールプロジェクトをサポートしている場合は、Windowsアプリケーションプロジェクトではなく、このプロジェクトタイプを選択できます。
Visual Studio NuGetパッケージを新しいツールセットバージョンに更新する必要がある
libpngをVisual Studio 2013にリンクしようとしてこの問題が発生しました。問題は、パッケージファイルにVisual Studio 2010および2012のライブラリしかなかったことです。
正しい解決策は、開発者が更新されたパッケージをリリースしてからアップグレードすることを期待することですが、VS2013の追加設定をハッキングしてVS2012ライブラリファイルを指定することでうまくいきました。
パッケージを(packages
ソリューションのディレクトリ内のフォルダーにある)パッケージを編集し、packagename\build\native\packagename.targets
そのファイルを見つけて、すべてのv110
セクションをコピーしました。条件フィールドのv110
をに変更しv120
ましたが、ファイル名のパスをすべてそのままにするように非常に注意していますv110
。これにより、Visual Studio 2013が2012年のライブラリにリンクできるようになり、この場合は機能しました。
数千の.cppファイルと数千の.hファイルを含むc ++で記述された大きなプロジェクトがあるとします。また、プロジェクトが10個の静的ライブラリに依存しているとしましょう。Windowsを使用していて、Visual Studio 20xxでプロジェクトをビルドするとします。Ctrl + F7 Visual Studioを押してソリューション全体のコンパイルを開始すると(ソリューションにプロジェクトが1つしかないと仮定)
コンパイルの意味は何ですか?
コンパイルの2番目のステップは、リンカーによって行われます。リンカーは、すべてのオブジェクトファイルをマージし、最終的に出力(実行可能ファイルまたはライブラリである可能性があります)をビルドする必要があります
プロジェクトをリンクする手順
error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
観察
この種のエラーを解決する方法
コンパイラー時間エラー:
リンカー時間エラー
#pragma once
コンパイルされている現在の.cppにすでにヘッダーが含まれている場合に、コンパイラーがヘッダーを1つも含まないようにするために使用します。最近この問題があり、それはVisual Studio Express 2013のバグであることがわかりました。バグを克服するために、プロジェクトからソースファイルを削除して再度追加する必要がありました。
コンパイラ/ IDEのバグであると思われる場合に試す手順:
最近のほとんどのリンカーには、さまざまな程度で出力する詳細オプションが含まれています。
gccおよびclangの場合。あなたは、一般的に追加し-v -Wl,--verbose
たり-v -Wl,-v
、コマンドラインに。詳細については、こちらをご覧ください。
MSVCの場合、/VERBOSE
(特に/VERBOSE:LIB
)がリンクコマンドラインに追加されます。
/VERBOSE
リンカーオプションに関するMSDNページ。リンクされた.libファイルは.dllに関連付けられています
同じ問題がありました。MyProjectとTestProjectというプロジェクトがあるとします。MyProjectのlibファイルをTestProjectに効果的にリンクしました。ただし、このlibファイルは、MyProjectのDLLがビルドされたときに作成されました。また、MyProjectのすべてのメソッドのソースコードは含まれていませんが、DLLのエントリポイントへのアクセスのみが含まれています。
この問題を解決するために、MyProjectをLIBとしてビルドし、TestProjectをこの.libファイルにリンクしました(生成された.libファイルをTestProjectフォルダーにコピーアンドペーストします)。その後、MyProjectをDLLとして再度ビルドできます。TestProjectがリンクされているlibにはMyProjectのクラスのすべてのメソッドのコードが含まれているため、コンパイルされます。
リンカエラーに関しては、この質問に人々が向けられるようですので、ここに追加します。
GCC 5.2.0でのリンカーエラーの1つの考えられる理由は、新しいlibstdc ++ライブラリABIがデフォルトで選択されるようになったことです。
std :: __ cxx11名前空間またはタグ[abi:cxx11]の型を含むシンボルへの未定義の参照に関するリンカーエラーが発生する場合は、_GLIBCXX_USE_CXX11_ABIの異なる値でコンパイルされたオブジェクトファイルをリンクしようとしていることを示している可能性があります大きい。これは通常、古いバージョンのGCCでコンパイルされたサードパーティライブラリにリンクするときに発生します。サードパーティライブラリを新しいABIで再構築できない場合は、古いABIでコードを再コンパイルする必要があります。
したがって、5.1.0以降にGCCに切り替えるときに突然リンカーエラーが発生する場合は、これを確認する必要があります。
リンカースクリプトをサポートしないGNU ldのラッパー
一部の.soファイルは実際にはGNU ldリンカースクリプトです。たとえば、libtbb.soファイルは次の内容のASCIIテキストファイルです。
INPUT (libtbb.so.2)
より複雑なビルドの中には、これをサポートしていないものもあります。たとえば、コンパイラオプションに-vを含めると、mainwin gccラッパーmwdipがリンクするライブラリの詳細出力リストにあるリンカースクリプトコマンドファイルを破棄することがわかります。簡単な回避策は、リンカースクリプト入力コマンドを置き換えることです。代わりにファイルのコピー(またはシンボリックリンク)を含むファイル、たとえば
cp libtbb.so.2 libtbb.so
それとも、のではなく、例えば、.soというのフルパスで-l引数を置き換えることができ-ltbb
DO/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
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はさらに右側のファイル内の定義を検索します。最終的に定義が見つかった場合、参照は解決されます。最後に参照が未解決のままである場合、リンケージは失敗します。リンカーは後方検索を行いません。
最初に、静的ライブラリを使用した例1my_lib.a
静的ライブラリは、オブジェクトファイルのインデックス付きアーカイブです。リンカが-lmy_lib
リンケージシーケンスで見つけ、これが静的ライブラリを参照している./libmy_lib.a
ことを理解した場合、リンカはプログラムにのオブジェクトファイルが必要かどうかを知りたいと考えますlibmy_lib.a
。
libmy_lib.a
つまりmy_lib.o
、にはオブジェクトファイルだけがあり、で定義されているの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
、プログラムに追加します。リンケージシーケンスのオブジェクトファイルは、常にプログラムに追加されます。これで、プログラムはへの参照を作成しhw
、の定義を含みませんhw
。ただし、リンケージシーケンスには、定義が不足している可能性のあるものは何も残っていません。参照するhw
まで終了し、未解決、および結合は失敗します。
2番目、例2、共有ライブラリlibz
共有ライブラリは、オブジェクトファイルなどのアーカイブではありません。これは、関数を持たず、代わりにそれが定義する他の複数のシンボルを公開するプログラムに非常によく似ているmain
ため、他のプログラムは実行時にそれらを使用できます。
多くのLinuxディストリビューションは本日、言語ドライバ(ように彼らのGCCツールチェーンを構成しgcc
、g++
、gfortran
(システムリンカに指示など)ld
リンクに)上の共有ライブラリなど、必要に応じて基礎。それらのディストリビューションの1つを入手しました。
これは、リンカが-lz
リンケージシーケンスで見つかり、これが共有ライブラリ(たとえば)を/usr/lib/x86_64-linux-gnu/libz.so
参照していることが判明した場合、プログラムに追加されている未定義の参照に、次のような定義があるかどうかを知りたいという意味ですによってエクスポートされたlibz
これが真の場合、リンカーはチャンクをコピーしてプログラムに追加しませんlibz
。代わりに、それはあなたのプログラムのコードを単にドクターします:
実行時、システムプログラムローダーはlibz
、プログラムのコピーを読み込むたびに、プログラムと同じプロセスにのコピーを読み込んで実行します。
実行時に、プログラムがで定義されているものを参照する場合は常に
libz
、その参照はlibz
同じプロセスののコピーによってエクスポートされた定義を使用します。
プログラムは、によってエクスポートされた定義を持つ1つのものlibz
、つまりでzlibVersion
一度だけ参照される関数を参照する必要がありeg2.c
ます。リンカがその参照をプログラムに追加し、によってエクスポートされた定義を見つけたlibz
場合、参照は解決されます
しかし、次のようにプログラムをリンクしようとすると、
gcc -o eg2 -lz eg2.o
イベントの順序は、リンカー発見時点で、実施例1と同様に、ちょうど同じように間違っている-lz
、存在しない全く彼らはで全てです:プログラムで何かへの参照eg2.o
、まだ見ていません。そのため、リンカはを使用しないと判断しますlibz
。に到達しeg2.o
、それをプログラムに追加した後、への未定義の参照があるzlibVersion
と、リンケージシーケンスは終了します。その参照は解決されず、リンケージは失敗します。
最後に、pkg-config
例2 のバリエーションには今や明白な説明があります。シェル展開後:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
になる:
gcc -o eg2 -lz eg2.o
これもまた例2です。
リンケージ:
gcc -o eg2 -lz eg2.o
うまくいきます!
(または、そのリンケージはFedora 23などではうまく機能しましたが、Ubuntu 16.04では失敗します)
これは、リンケージが機能するディストリビューションが、共有ライブラリを必要に応じてリンクするようにGCCツールチェーンを構成しないディストリビューションの1つだからです。
かつて、UNIXライクなシステムでは、静的ライブラリと共有ライブラリを異なるルールでリンクするのが普通でした。リンケージシーケンスの静的ライブラリは、例1で説明した必要に応じてリンクされましたが、共有ライブラリは無条件にリンクされました。
リンカはプログラムで共有ライブラリが必要かどうかを考える必要がないため、この動作はリンク時に経済的です。共有ライブラリの場合はリンクします。そして、ほとんどのリンケージのほとんどのライブラリは共有ライブラリです。しかし、欠点もあります:-
実行時に共有ライブラリを必要としない場合でも、プログラムと一緒に読み込まれる可能性があるため、これは実行時に不経済です。
静的ライブラリと共有ライブラリの異なるリンケージルール-lfoo
は、リンケージがに解決されるの/some/where/libfoo.a
かに解決されるのか/some/where/libfoo.so
わからず、共有ライブラリと静的ライブラリの違いを理解できない未熟なプログラマを混乱させる可能性があります。
このトレードオフは、今日の分裂的な状況につながっています。一部のディストリビューションでは、必要に応じて 原則をすべてのライブラリに適用できるように、共有ライブラリのGCCリンケージルールを変更しています。一部のディストリビューションは古い方法に固執しています。
私がやった場合:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
確かにeg1.c
最初にgccをコンパイルしてから、結果のオブジェクトファイルをにリンクする必要がありますlibmy_lib.a
。それでは、リンクを行うときにオブジェクトファイルが必要であることをどのようにして知ることができないのでしょうか。
単一のコマンドでコンパイルおよびリンクしても、リンケージシーケンスの順序は変更されないためです。
上記のコマンドを実行すると、gcc
コンパイル+リンケージが必要であることがわかります。だから、舞台裏、それはコンパイルコマンドを生成し、それを実行し、その後、リンクコマンドを生成し、そしてかのように、それを実行し、あなたが 2つのコマンドを実行していました:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
だから、リンケージは、あなたがあればそれと同じように失敗しないこれらの2つのコマンドを実行します。この失敗で気が付く唯一の違いは、gccがcompile + linkの場合に一時オブジェクトファイルを生成したことですeg1.o
。これは、使用するように指示していないためです。私たちは見る:
/tmp/ccQk1tvs.o: In function `main'
の代わりに:
eg1.o: In function `main':
相互に依存するリンクライブラリが指定されている順序が間違っている
相互に依存するライブラリを間違った順序で配置することは、リンケージの中で、定義を提供するファイルよりも後に来るものの定義を必要とするファイルを取得できる1つの方法にすぎません。ライブラリを参照するオブジェクトファイルの前にライブラリを置くことも、同じ間違いを犯す別の方法です。
フレンド演算子(または関数)を含むテンプレートタイプのコードスニペットが与えられます。
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の最も内側を囲む名前空間のメンバーになりますが、名前空間スコープでの一致する宣言がない限り、ルックアップ(Xを考慮する引数依存ルックアップを除く)にはアクセスできません提供されました...
cppreferenceとC ++ 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)
ヘッダーファイルとそれに関連付けられた共有ライブラリ(.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
独自のプログラムで使用する場合、何が起こったのかと思ってヘッダーをひっくり返すことになります(しゃれつもり)。
これが現実の世界でどのように起こり得るかの例を以下に説明します。
私には2つのプロジェクトがgraphics.lib
ありmain.exe
ます。どちらのプロジェクトもに依存してい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(...);
}
ブーム!リンカエラーが発生し、それが失敗する理由がわかりません。その理由は、共通ライブラリが同じインクルードの異なるバージョンを使用しているためですcommon_math.h
(ここでは、例で異なるパスを含めることでそれを明確にしましたが、必ずしもそれほど明確ではない場合があります。インクルードパスはコンパイラ設定で異なる場合があります)。 。
この例ではdraw()
、実際にはライブラリによってエクスポートされていることがわかっている場合でも、リンカーはを見つけられなかったと通知します。何がうまくいかないのかと頭を悩ませるのに何時間も費やす可能性があります。問題は、パラメーターの型がわずかに異なるため、リンカーは異なるシグネチャを参照することです。この例でvec3
は、コンパイラに関する限り、両方のプロジェクトでタイプが異なります。これは、2つのわずかに異なるインクルードファイルからのものである可能性があります(インクルードファイルは、2つの異なるバージョンのライブラリからのものである可能性があります)。
Visual Studioを使用している場合、DUMPBINはあなたの友達です。他のコンパイラにも同様のツールがあると思います。
プロセスは次のようになります。
[1]プロジェクトとは、ライブラリまたは実行可能ファイルを生成するためにリンクされた一連のソースファイルを意味します。
編集1:理解しやすくなるように最初のセクションを書き直しました 他に修正が必要な場合は、以下にコメントしてください。ありがとう!
UNICODE
定義A WindowsのUNICODEビルドがで構築されたTCHAR
ように定義されているなどwchar_t
でビルドしない場合などUNICODE
に、ビルドとして定義TCHAR
として定義されchar
、これらなどUNICODE
と_UNICODE
定義は、すべての影響を与える「T
」文字列型を、LPTSTR
、LPCTSTR
そして彼らのヘラジカ。
UNICODE
定義済みのライブラリを1つ構築し、それがUNICODE
定義されていないプロジェクトでリンクしようとすると、の定義に不一致があるため、リンカーエラーが発生しTCHAR
ます。char
対wchar_t
。
エラーには通常、関数、char
またはwchar_t
派生型の値が含まstd::basic_string<>
れますが、これらにも同様に含まれる可能性があります。コード内の影響を受ける関数を参照するときTCHAR
、std::basic_string<TCHAR>
などへの参照がよくあります。これは、コードが最初にUNICODEとマルチバイト文字(または「狭い」)ビルドの両方を意図していたことを示す証拠です。
これを修正するには、UNICODE
(および_UNICODE
)の一貫した定義を使用して、必要なすべてのライブラリとプロジェクトをビルドします。
これはどちらかで行うことができます。
#define UNICODE
#define _UNICODE
またはプロジェクト設定で;
プロジェクトプロパティ>一般>プロジェクトデフォルト>文字セット
またはコマンドラインで。
/DUNICODE /D_UNICODE
代替手段も適用可能です。UNICODEを使用する予定がない場合は、定義が設定されていないこと、および/またはプロジェクトで複数文字の設定が使用され、一貫して適用されていることを確認してください。
「リリース」ビルドと「デバッグ」ビルドの間でも一貫していることを忘れないでください。
ビルドの「クリーン」は、以前のビルド、失敗したビルド、不完全なビルド、およびその他のビルドシステム関連のビルドの問題から取り残されている可能性がある「デッドウッド」を削除できます。
一般に、IDEまたはビルドには何らかの「クリーン」機能が含まれますが、これは正しく構成されていない場合(手動のmakefileなど)、または失敗する場合があります(中間または結果のバイナリが読み取り専用など)。
「クリーン」が完了したら、「クリーン」が成功し、生成されたすべての中間ファイル(たとえば、自動化されたmakefile)が正常に削除されたことを確認します。
このプロセスは最終的な手段と見なすことができますが、多くの場合、最初のステップとして適切です。特に、エラーに関連するコードが最近追加された場合(ローカルまたはソースリポジトリから)。
const
変数の宣言/定義に「extern」がない(C ++のみ)C出身の人にとって、C ++ではグローバルconst
変数に内部(または静的)リンケージがあることは驚くかもしれません。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
これはかなり古い質問ですが、複数の回答が受け入れられていますが、あいまいなものを解決する方法を共有したいと思います「未定義の参照」エラーます。
私はエイリアスを使用して参照していました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つのファイルがあるとします。
main.cppとfile.hで使用されているさまざまなライブラリに注意してください。main.cpp #includeされた「file.h」が< filesystem >の後にあるため、そこで使用されているファイルシステムのバージョンはC ++ 17のものでした。以前は、次のコマンドを使用してプログラムをコンパイルしていました。
$ 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をリンクします
この方法は、任意の関数ことfile.oに含まれ、main.oで使用される必要がpath_t
あるため、「未定義参照」エラーを与えmain.oを呼ぶstd::filesystem::path
が、file.oしますstd::experimental::filesystem::path
。
これを修正するには、file.hの<experimental :: filesystem>を<filesystem>に変更する必要がありました。
gccのデフォルトの動作では、すべてのシンボルが表示されます。ただし、翻訳単位がoption -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
nm
オプション-C
と一緒に使用して名前をデマングルすることもできます(C ++が使用されている場合)。
Windows-dllと同様に、たとえばDLL_PUBLIC
次のように定義された定義でパブリック関数をマークします。
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
これは、おおよそWindows / MSVCバージョンに対応しています。
#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
異なるアーキテクチャ
次のようなメッセージが表示される場合があります。
library machine type 'x64' conflicts with target machine type 'X86'
その場合、利用可能なシンボルは、コンパイル対象のアーキテクチャとは異なるアーキテクチャのものであることを意味します。
Visual Studioでは、これは誤った「プラットフォーム」が原因であり、適切なものを選択するか、適切なバージョンのライブラリをインストールする必要があります。
Linuxでは、誤ったライブラリフォルダーが原因である可能性があります(たとえば、のlib
代わりにlib64
を使用)。
MacOSでは、両方のアーキテクチャを同じファイルで出荷するオプションがあります。リンクは両方のバージョンが存在することを期待している可能性がありますが、1つだけです。また、ライブラリが取得される場所が間違っているlib
/ lib64
フォルダーの問題である可能性もあります。