C ++についてテンプレートクラスのテンプレート関数をヘッダーファイルにしか記述できない理由

転載元:https://www.yht7.com/news/27097

コーディングから実行まで
まず、C ++プロジェクトがテキストファイルから実行可能ファイルを生成する方法について説明します。

事前コンパイル
事前コンパイルは、#include、#define、#ifdef、#pragmaなどの事前コンパイルされたマクロコマンドを処理することです。

コンパイル
コンパイルとは、テキスト内のソースコードを、コンピューターで直接処理できるアセンブリ言語に変換することです。ここで、C ++は個別のコンパイルを使用することに注意してください(各hファイルとcppファイルのコンパイルは独立しています)。

C ++開発は、個別の宣言と実現の開発形式で広く使用されています。コンパイルプロセスは個別のコンパイルです。つまり、各cppファイルが完全に個別にコンパイルされ、次に各objオブジェクトファイルが生成され、最後に実行可能なexeファイルが生成されます。リンカーリンクを介して。引用元:https://blog.csdn.net/uestclr/article/details/51372780

Linking
Compilationは、C ++が次のコードのような個別のコンパイルを使用することを述べました。

Test.h

void test();

Test.cpp

#include "Test.h"
void test() {
    
    
    //todo
}

main.cpp

#include "test.h"
int main() {
    
    
    test();
    return 0;
}

これまでのところ、コンパイル中に2つのファイルを生成する必要があります:( macOSの下で)

Test.cpp.o
main.cpp.o
のコンパイル中に、main.cppがtest()関数を呼び出すと、test()関数の関数エントリアドレスがわかりません。コンパイラは関数シンボル(test ())シンボルインポートテーブルに(.aファイルの形式は実行可能ファイルと同じであるため、そのようなファイルにはシンボルインポートテーブルとシンボルエクスポートテーブル(インポートテーブルとエクスポートテーブル)があり、これらはすべてを関連付けますシンボルとそのア​​ドレス)。

このように、リンカはTest.cpp.oのシンボルエクスポートテーブルでシンボルtest()のアドレスを見つけて、オフセット処理を行うだけで済みます(2つの.oファイルがマージされるため、もちろんアドレス特定のオフセットがあり、このリンカーは明確です)main.cpp.oのシンボルインポートテーブルにtest()が占める項目を書き込みます。

テンプレート関数
冒頭で説明したトピックに戻ると、テンプレート関数の宣言と定義は2つのファイルに記述されており、コンパイラがリンクするとエラーが報告されます。具体例は次のとおりです。

Test.h

template<class T, T t>
void test(T a);

Test.cpp

#include "Test.h"
template<class T, T t>
void test(T a) {
    
    
    //todo
}

main.cpp

#include "Test.h"
int main() {
    
    
    test<int, 1>();
    return 0;
}

コンパイラがエラーを報告しました:

[ 18%] Linking CXX executable cpp_learning
Undefined symbols for architecture x86_64:
  "void test<int, 1>(int)", referenced from:
      main() in main.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

それを見て、リンクフェーズのエラーを判別してください。

どうして?

テンプレート関数の制限
C ++標準では、テンプレートを使用しない場合はインスタンス化しないように明記されています。これは、Test.cpp.oにテスト関数の定義がないことを意味します。

別のコンパイル環境では、コンパイラは特定の.cppファイルをコンパイルするときに別の.cppファイルの存在を認識せず、それを検索しません(保留中のシンボルに遭遇すると、リンカーに期待を固定します)。このモードはテンプレートがなくてもうまく機能しますが、テンプレートは必要な場合にのみインスタンス化されるため、テンプレートに遭遇すると唖然とします。そのため、コンパイラはテンプレートの宣言のみを確認すると、インスタンス化できません。テンプレートを変換するには、外部接続でのみシンボルを作成でき、リンカがシンボルのアドレスを解決することを期待できます。ただし、テンプレートを実装する.cppファイルでテンプレートインスタンスが使用されていない場合、コンパイラはそれをインスタンス化するには遅すぎます。したがって、プロジェクト全体の.aにはテンプレートインスタンスのバイナリコードが1行もありません。 、したがってリンカーも使い果たされます。----参照:https://www.jianshu.com/p/dc94f0cbfcf7

解決策
は次のとおりです。

テンプレート関数の定義をhファイルに書き込みます
。cppファイルでテンプレート関数を使用します(コンパイル時にテンプレート関数をインスタンス化できるようにするため)。

おすすめ

転載: blog.csdn.net/sinat_27382047/article/details/107739562