2012-01-28 5 views
3

のは、私は次のコードがあるとしましょう:テンプレートクラスは、C++のさまざまなコンパイル単位で複数回コンパイルされていますか?

// templateClass.h 
#ifndef TEMPLATE_CLASS_H 
#define TEMPLATE_CLASS_H 

template <typename T> 
class tClass 
{ 
public: 
    tClass(); 
}; 

#endif 

// templateClassDef.inl 
#ifndef TEMPLATE_CLASS_DEF_INL 
#define TEMPLATE_CLASS_DEF_INL 

template <typename T> 
tClass<T>::tClass() 
{ 
} 

#endif 

// normalClass.h 
#include "templateClass.h" 

class normal 
{ 
public: 
    normal(); 
}; 

// normalClass.cpp 
#include "normalClass.h" 
#include "templateClassDef.inl" 

normal::normal() 
{ 
    tClass<int> a; 
} 

// main.cpp 
#include "templateClass.h" 
#include "templateClassDef.inl" 

#include "normalClass.h" 

int main() 
{ 
    tClass<int> a; 
    normal b; 

    return 0; 
} 

注意inlファイルは、それが通常であるように、ヘッダに含まれていませんが、その代わりに、ソースファイルに含まれていることを(私はそれがない承知しています標準的な方法...これは単なる例です)。 normalcClass.cpptClass<int>をインスタンス化しているので、main.cppであることに注意してください。

私は両方のインスタンスが別々の翻訳単位で発生していてもコンパイラは、テンプレートクラスから、それはそれは同じタイプ(すなわちtClass<int>)で考えると、明示的なインスタンスに遭遇するたびに、インスタンスを構築するために持っているかどうかの好奇心(normalClass.cppおよびmain.cpp)?また、これはコンパイル時に増加するでしょうか(前の質問に対する答えがであれば、それは再びをインスタンス化します)、これもはいであるべきですか?

+0

これはコンパイラに依存しますが、答えは両方の質問に対してyesです –

答えて

2

一般的に、テンプレートクラスは、通常、コンパイラが遭遇するたびにコンパイルされます。

+0

コンパイラは新しいクラスをどこかに保存しませんし、新しいクラスをコンパイルするのではなく、すでにコンパイルされたクラスを使用します。そして、新しい型を何度も作成するのであれば、_same class nameとdefinition_のようなものを言って、なぜそれはエラーにならないのですか? – Samaursa

+0

@Samaursa:これについて私を引用してはいけませんが、高度な[テンプレートメタプログラミング](http://en.wikipedia.org/wiki/Template_metaprogramming)のテクニックをサポートするために、いくつかのコンパイラが実際にテンプレートのインスタンス化をキャッシュしていると思います。しかし、私はコンパイラがいずれかの方法でチェックして、1つの定義ルールの違反を診断すると思います。 –

+1

リンカは各インスタンシエーションのコピーを1つだけ残して削除します。しかし、コンパイラはリンクする他のモジュールを知ることができないので、リンク時に 'extern'を使って提供することを約束しない限り、使用するインスタンス化をコンパイルする必要があります。インライン関数のようなテンプレートは、複数定義の競合から免除されます。リンカーは使用するために1つのコピーを選択します。 ODRルールは引き続き適用され、同じ名前のすべてのコピーが同じ動作をしている必要があります。そのため、リンカが選択するものは重要ではありません。 –

3

基本的に、テンプレートはコンパイル単位ごとにインスタンス化されるため、コンパイル時間が長くなります。明示的なインスタンス化や外部化のように、この問題を処理するための新しいC++標準の拡張機能と機能がいくつかあります。いくつかの説明と最適化技術のために、このドキュメントを参照してください。

http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html

+1

コンパイル単位ごと、およびテンプレート引数セットごとに*コンパイルされているため、コンパイル時間が長くなります。そして、さまざまな議論のためのインスタンス化がバイナリを膨らませる可能性があります。しかし、複数のコンパイル単位のためにバイナリに同じインスタンスが複数回現れることは聞いたことがありません。 –

+0

はい、そうでなければ署名が衝突します。私は、コンパイラ自体がどのインスタンス化が実際に使用されているかを知ることができず、したがって、すべてのインスタンスがコンパイル単位ごとに作成/参照されたことを指摘したいと思います。私は最近、大きすぎる共有ライブラリに問題がありました。静的リンクは私のためにそれをしました。 – Sam

+0

しかし、 "あなたのバイナリを膨らませる"部分は誤解を招きます。 –

1

コンパイラはコンパイラ次第であるテンプレートのインスタンスメカニズムを実装する方法。実際には、必要な視覚的なテンプレート定義は、翻訳単位で使用されるときに作成されます。これにより、かなりの不必要な作業が発生する可能性があります。たとえば、翻訳ユニットでIOストリームを使用するたびに、ストリームクラスとロケールクラスのすべての対応する関数がインスタンス化されます。 ...常に同じ2つのタイプで!

テンプレートが何であるかによって、実装をヘッダーではなくソースファイルに置き、ソースでテンプレートを明示的にインスタンス化することができます。異なるインスタンス化が使用されている場合、これは実行可能ではありません。この場合、一般的に使用されるインスタンス化を事前定義することは合理的かもしれません。このために、C++ 2011でインスタンス化をexternに宣言することができます。もちろん、対応する関数やクラスをどこかで明示的にインスタンス化する必要があります。

1

はい、コンパイル単位は互いに独立してコンパイルされます。

しかし、多くのツールチェーンではこれを回避するプリコンパイル済みヘッダーがサポートされているため、コンパイラは別のコンパイル単位で既に処理されたコードを再利用できます。

関連する問題