なぜC ++コンパイラは、テンプレートクラスの分割コンパイルをサポートしていません。

私たちは宣言したクラステンプレートを定義するときは、そうでない場合、コンパイラは文句を言うだろう、ファイル内の宣言と定義をしなければなりません。

これは、1つの理由、それは何であるのでしょうか?なぜこのようにですか?

まず、符号化部(翻訳単位)を、その.cppファイルを含むように拡張される.cppファイルおよびすべての.hファイル、.hファイルの#includeコード、コンパイラを指し.cpp .OBJファイル(ファイルとして当社のプラットフォームであると仮定するとWin32の PE(ポータブル実行可能ファイル、すなわちWindows実行)ファイルフォーマットを有する)、及びそれ自体に含ま既にバイナリコードであり、必ずしも実行することができません、主な機能という保証があってはならないので。場合は、別の.cpp内のすべてのファイルが完了した後、.exeファイルとしてコネクタ(リンカ)で接続されたコンパイル済みのプロジェクトにコンパイラに。

例えば:

//---------------test.h-------------------//
void f();//这里声明一个函数f

//---------------test.cpp--------------//
#include”test.h”
void f()
{
    …//do something
}//这里实现出test.h中声明的f函数
 
//---------------main.cpp--------------//
#include”test.h”
int main()
{
    f();//调用f,f具有外部连接类型
}

この例では、試験。CPPのmain.cppに、それぞれが異なる.OBJファイル(仮称TEST.OBJとMAIN.OBJ)にコンパイルされるが、main.cppには、関数fを呼び出す、しかし、コンパイラのコンパイルmain.cppには、それが唯一のちょうどTEST.Hファイルmain.cppには、ボイドF()について知っているに含まれているとき、コンパイラはここになりますので、声明考えられている外部接続型とみなしfをその機能の実装である別のファイル、すなわちTEST.OBJ本実施形態では、.OBJコードF関数の単一ライン上の実際MAIN.OBJバイナリコードせず、そしてこれらのコードはTEST.CPPに実際に存在しますでTEST.OBJにコンパイル。MAIN.OBJではFへの呼び出しは次のように、ラインコールコマンドを生成します。

コール・F [C ++は名前の符号化には当然の結果である[た]扱われています]

実装コードでMAIN.OBJ F何行が存在しないため、コンパイル時には、この呼び出し命令は、明らかに間違っています。どのようにそれを行うには?これは、コネクタのタスクで、コネクタが実装コードfを探して(この場合はTEST.OBJ内)の他の.objを担当して、コールFコールエントリ・ポイント・アドレスF実際の関数にこのディレクティブの後に住所を見つけます。注意:コネクタは、実際の.obj「接続」で働くには、.exeファイルとなっており、それが最も重要なタスクが上と言われている、別の.OBJのアドレスで外部接続シンボルの外観、その後、オリジナルの「偽」のアドレスを交換してください。

このプロセスは、より深い場合には:

コマンドラインFコールは、それが実際にいわゆるスタブ、JMP 0xABCDEFがあり、実際にはそうではありません。アドレスは任意でよいが、キーはアクションF本当の呼び出しに命令アドレスのラインを持っていることです。言い換えれば、Fへのすべての呼び出し内でこの.objファイルには、そこが本当に「コール」は、F、同じアドレスにJMPあります。これの利点は、コネクタがいる限り、後者のは、ライン上でそのままXXXアドレスを呼び出すように、アドレスを変更するとき。しかし、コネクタは.OBJと.EXE形式が同じであるため、シンボルインポートテーブル内のシンボルがある(本実施例では、これはTEST.OBJである)Fの実際のアドレスにそれを検索する方法であって、そのような文書エクスポートテーブル(インポートテーブルおよびエクスポートテーブル)ここで、すべてのシンボルとそれに関連するアドレス。二つはもちろんの.objファイルを、組み合わせているため限りTEST.OBJエクスポートシンボルテーブルFシンボルを見コネクタ(もちろんC ++のFマングリング製)、リスト上のアドレスとして、プロセスの数(のためのオフセット、いくつかのオフセットアドレス、1はfで占有できることMAIN.OBJシンボルインポートテーブル内のコネクタ明白な)書き込みがあるでしょう。

これはおそらくプロセスです。キーは、この次のとおりです。

main.cppにコンパイルすると、コンパイラが実装さfを知らないので、それはそれへの呼び出しに来るときだけでコネクタ本体fがその実現のためになるはずという指示を与えます。この手段は、Fの行にはMAIN.OBJバイナリコードが存在しないこと。

TEST.CPPをコンパイルする場合、コンパイラはFの実現を発見しました。実現(バイナリコード)fはTEST.OBJ年に発生して以来。

接続時に、TEST.OBJに(シンボルテーブルを導出することによって)コード(バイナリ)アドレスF接続見出します。そして、実際のアドレスFに未処理の呼び出しXXXアドレスをMAIN.OBJ。コンプリート。

しかし、テンプレートのコードを、あなたは実際には、知っている、とテンプレート関数は、直接プロセスの「インスタンス」でなければなりませんバイナリコードに翻訳することはできません。

例えば:

//----------main.cpp------//
template<class T>
void f(T t){}
int main()
{
    …//do something
    f(10);//call f<int> 编译器在这里决定给f一个f<int>的实例
    …//do other thing
}

あなたはfがなり、main.cppにファイルFをオフに呼び出さなかった場合つまり、インスタンス化されないので、F上のバイナリコードの任意の行にはMAIN.OBJはありません!あなたは、この呼び出しのような場合:

F(10); // F <整数>が出てインスタンス化されます

F(10.0); // F <ダブル>それがインスタンス化されます

MAIN.OBJ F <整数> F <ダブル>バイナリコードセグメントの二つの機能を有することになるようにします。などなど。

しかし、インスタンス化コンパイラは、テンプレートを知っている必要が定義されたが、そうではありませんか?

(テンプレート宣言と分離の実装)以下の実施例を参照してください。

//-------------test.h----------------//
template<class T>
class A
{
public:
    void f(); // 这里只是个声明
};
 
//---------------test.cpp-------------//
#include”test.h”
template<class T>
void A<T>::f() // 模板的实现
{
    …//do something
}
 
//---------------main.cpp---------------//
#include”test.h”
int main()
{
    A<int> a;
    f(); // #1
}

コンパイラでは#1は知りませんA <整数> :: FそれはTEST.H内部は、コンパイラは、コネクタを願うことができるように、それは希望A <整数>その他の内部の.objファイルを見つけることができる、ではないとして、定義されています例:: Fは、本実施形態では後者の、しかし、実際にはA <整数> :: FバイナリコードTEST.OBJでありますか?NO!C ++標準では、それはそれを明らかにしたので、侯テンプレートは、それが出てインスタンス化するべきではありません使用されていない場合、TEST.CPPはまだA <整数> :: Fで使用されますか?いいえ!だから、TEST.OBJ A :: F上の文書の事実TEST.CPP翻訳であり、バイナリコードのない行がないので、ボディスのコネクタは、接続エラーを与えていました。しかし、この時点で(TEST.CPP中)、コンパイラは、テンプレートの定義を知っているので、コールA <整数> :: F、コンパイラは、それをインスタンス化しますTEST.CPPで、あなたが書いている場合機能、コネクタは、タスクを完了することができますので、それをインスタンス化することができ、その後、TEST.OBJシンボルは、エクスポートテーブルアドレスA <整数> :: Fシンボルを持っています。

キーは次のとおりです。個別のコンパイル環境では、コンパイラのコンパイルの.cppファイル1は、保留中の接続記号に直面したとき(それが望んでいるだろう見つけることではない、別の.cppファイルの存在を知りませんデバイス)。このモードでは、テンプレートなしでうまく動作しますが、ボディス上のテンプレートの顔、必要なだけのテンプレートが出てインスタンス化しますので、コンパイラは宣言のみテンプレートを見ることができるときに、それがインスタンス化することはできませんテンプレートの、あなただけのサイン、外部との接続を作成することができ、コネクタのうち、解像度を楽しみにしてシンボルを解決することができます。テンプレートのテンプレートインスタンスの.cppファイルの実装を使用しない場合は、プロジェクト全体がテンプレートの.objインスタンスバイナリコードの行を見つけることができませんので、しかし、コンパイラは、インスタンス化するために、わざわざ、そのコネクタはまた、トリックが不足しています。

发布了3 篇原创文章 · 获赞 0 · 访问量 99

おすすめ

転載: blog.csdn.net/qq_33669963/article/details/103986391