テンプレートをコンパイルするためには、C++ コンパイラは従来の UNIX コンパイラよりも多くのことを行う必要があります。C++ コンパイラは、必要に応じてテンプレートインスタンスのオブジェクトコードを生成しなければなりません。コンパイラは、テンプレートレポジトリを使って、別々のコンパイル間でテンプレートインスタンスを共有することができます。また、テンプレートコンパイルのいくつかのオプションを使用できます。コンパイラは、別々のソースファイルにあるテンプレート定義を見つけ、テンプレートインスタンスと main コード行の整合性を維持しなければなりません。
フラグ -verbose=template が指定されている場合は、テンプレートコンパイル作業中の重要なイベントがユーザーに通知されます。逆に、デフォルトの -verbose=no%template が指定されている場合は、通知されません。その他に、 +w オプションを指定するとテンプレートインスタンス化が行われたときに問題になりそうな内容が通知される場合があります。
テンプレートレポジトリの管理は CCadmin(1) コマンドで行います。たとえば、プログラムの変更によって、インスタンス化が不要になり、記憶領域が無駄になることがあります。CCadmin -clean コマンド (以前のリリースの ptclean) を使用すれば、すべてのインスタンス化と関連データを整理できます。インスタンス化は、必要なときだけ再作成されます。
コンパイラには、インスタンスの配置とリンケージの方法として、外部、静的、大域、明示的、半明示的のどれを使うかを指定できます。
外部インスタンスはすべての開発に適しており、テンプレートのコンパイルとしては総合的に最も優れています。
静的インスタンスは非常に小さなプログラムやデバッグに適しており、用途は限られています。
大域インスタンスは、ある種のライブラリ構造に適しています。
明示的インスタンスは、厳密に管理されたアプリケーションコンパイル環境に適しています。
半明示的インスタンスは、それより多少管理の程度が緩やかなアプリケーションコンパイル環境に適しています。ただし、このインスタンスは明示的インスタンスより大きなオブジェクトファイルを生成し、用途は限られています。
特別な理由がない限り、デフォルトの外部インスタンス方式を使用してください。詳細は、『C++ プログラミングガイド』を参照してください。
外部インスタンスの場合では、すべてのインスタンスがテンプレートレポジトリ内に置かれます。テンプレートインスタンスは 1 つしか存在できません。つまり、インスタンスが未定義であるとか、重複して定義されているということはありません。テンプレートは必要な場合にのみ再インスタンス化されます。
テンプレートインスタンスは、レポジトリ内では大域リンケージを受け取ります。インスタンスは、外部リンケージで現在のコンパイル単位から参照されます。
外部リンケージは、-instances=extern オプションで指定します。このオプションはデフォルトです。
インスタンスはテンプレートレポジトリ内に保存されているので、外部インスタンスを使用する C++ オブジェクトをプログラムにリンクするには CC コマンドを使用しなければなりません。
使用するすべてのテンプレートインスタンスを含むライブラリを作成したい場合には、CC コマンドに -xar オプションを指定してください。ar コマンドは使用できません。次に例を示します。
%demo CC -xar -o libmain.a a.o b.o c.o
詳細は、第 6 章「ライブラリの構築」を参照してください。
静的インスタンスの場合は、すべてのインスタンスが現在のコンパイル単位内に置かれます。その結果、テンプレートは各再コンパイル作業中に再インスタンス化されます。インスタンスはテンプレートレポジトリに保存されません。
インスタンスは静的リンケージを受け取ります。これらのインスタンスは、現在のコンパイル単位以外では認識することも使用することもできません。そのため、テンプレートの同じインスタンス化がいくつかのオブジェクトファイルに存在することがあります。これには、次の欠点があります。
複数のインスタンスによって不必要に大きなプログラムが生成されます (したがって、静的インスタンスのリンケージは、テンプレートがインスタンス化される回数が少ない小さなプログラムだけに適しています)。
静的変数を持つテンプレートにはその変数のコピーがたくさんあります。これは必然的に C++ 標準に違反することになります。したがって、静的インスタンスはテンプレート内の静的変数には使用できません。
静的インスタンスは潜在的にコンパイル速度が速いため、修正継続機能を使用したデバッグにも適しています (『dbx コマンドによるデバッグ』を参照してください)。
静的インスタンスリンケージは、-instances=static コンパイルオプションで指定します。静的インスタンスリンケージは、定義取り込み型テンプレート編成 (テンプレートを使用するファイルの中にテンプレートの宣言と定義が含まれている編成) でのみ使用することができます。コンパイラは定義を検索しません (『C++ プログラミングガイド』を参照してください)。
大域インスタンスの場合では、すべてのインスタンスが現在のコンパイル単位の中に置かれます。その結果、テンプレートは各再コンパイル作業中に再インスタンス化されます。テンプレートはテンプレートデータベースに保存されません。
テンプレートインスタンスは大域リンケージを受け取ります。これらのインスタンスは現在のコンパイル単位以外でも認識したり、使用したりできます。その結果、複数のコンパイル単位におけるインスタンス化でリンク作業中に複数のシンボル定義のエラーが生じることがあります。したがって、大域インスタンスは、インスタンスが繰り返されないことがわかっている場合に限り適しています。
大域インスタンスは、-instances=global オプションで指定します。
大域インスタンスは、定義取り込み型テンプレート編成でのみ使用することができます。コンパイラは定義を検索しません。
明示的インスタンスの場合、インスタンスは、明示的にインスタンス化されたテンプレートに対してのみ生成されます。暗黙的なインスタンス化は行われません。インスタンスは現在のコンパイル単位内に置かれるため、テンプレートは再コンパイルのたびに再インスタンス化され、テンプレートレポジトリには保存されません。
テンプレートインスタンスは大域リンケージを受け取ります。これらのインスタンスは、現在のコンパイル単位の外でも認識でき、使用できます。同じプログラムで複数の明示的なインスタンス化があると、リンカーで複数シンボル定義エラーになります。したがって、明示的インスタンス方式は、明示的なインスタンス化でライブラリを構成する場合のように、インスタンスが繰り返されないことがわかっている場合に限り適しています。
明示的インスタンスは、-instances=explicit オプションで指定します。
明示的インスタンスリンケージは、定義取り込み型テンプレート編成でのみ使用できます。コンパイラは定義を検索しません。
半明示的インスタンスの場合、インスタンスは、明示的にインスタンス化されるテンプレートやテンプレート本体の中で暗黙的にインスタンス化されるテンプレートに対してのみ生成されます。main コード行内で行う暗黙的なインスタンス化は不完全になります。インスタンスは現在のコンパイル単位に置かれます。したがって、テンプレートは再コンパイルごとに再インスタンス化され、テンプレートレポジトリには保存されません。
明示的インスタンスは大域リンケージを受け取ります。これらのインスタンスは、現在のコンパイル単位の外でも認識でき、使用できます。同じプログラムで複数の明示的インスタンス化があると、リンカーで複数のシンボル定義エラーになります。したがって、半明示的インスタンスは、明示的なインスタンス化によってライブラリを構成する場合のように、明示的インスタンスが繰り返されないことがわかっている場合にだけ適しています。
明示的インスタンスの本体内から使用される暗黙的インスタンスは、静的リンケージを受け取ります。これらのインスタンスは現在のコンパイル単位の外では認識できません。そのため、テンプレートの同じインスタンス化がいくつかのオブジェクトファイルに存在することがあります。これには、次の 2 つの欠点があります。
複数のインスタンスによって不必要に大きなプログラムが生成されます (したがって、半明示的インスタンスのリンケージは、テンプレート本体で複数のインスタンス化が起こらないプログラムだけに適しています)。
静的変数を持つテンプレートにはその変数のコピーがたくさんあります。これは必然的に C++ 標準に違反することになります。したがって、半明示的インスタンスはテンプレート内の静的変数には使用できません。
半明示的インスタンスは、-instances=semiexplicit オプションで指定します。
半明示的インスタンスリンケージは、定義取り込み型テンプレート編成でのみ使用できます。コンパイラは定義を検索しません。
テンプレートレポジトリには、別々のコンパイルで共有できるテンプレートインスタンスが格納されます。したがって、テンプレートインスタンスは必要なときだけコンパイルされます。テンプレートレポジトリには、外部インスタンスを使用したときにテンプレートのインスタンス化に必要な、ソースファイル以外のすべてのファイルが含まれています。このレポジトリはその他の種類のインスタンスには使用されません。
テンプレートレポジトリは、デフォルトで、Sun WorkShop のキャッシュディレクトリ (SunWS_cache) にあります。Sun WorkShop のキャッシュディレクトリは、出力ファイルが置かれるのと同じディレクトリ内にあります。SUNWS_CACHE_DIR 環境変数を設定すれば、キャッシュディレクトリ名を変更できます。
コンパイラは、テンプレートインスタンスを格納しなければならないとき、出力ファイルに対応するテンプレートレポジトリにそれらを保存します。たとえば、次の例では、オブジェクトファイルを ./sub/a.o に書き込み、テンプレートインスタンスを ./sub/SunWS_cache にあるレポジトリに書き込みます。
demo% CC -o sub/a.o a.cc
コンパイラがテンプレートをインスタンス化するときにこのキャッシュディレクトリが存在しない場合は、このディレクトリが作成されます。
-ptrdirectory オプションでレポジトリを置くディレクトリを指定できます。
コンパイラは、読み取るオブジェクトファイルに対応したテンプレートレポジトリから読み取りを行います。たとえば次の例では、/sub1/SunWS_cache と ./sub2/SunWS_cache から読み取り、必要なら ./SunWS_cache へ書き込みます。
demo% CC sub1/a.o sub2/b.o
複数の -ptr オプションを使って複数のレポジトリを指定しないでください。-ptr では 1 つのレポジトリ位置しか指定できません。
テンプレートレポジトリを複数のプログラムやライブラリで共有しないでください。たとえば、次のようには指定しないでください。
demo% CC a.cc demo% CC b.cc
この指定をすると、整合性のない結果と予期できないリンクエラーが発生します。
したがって、実際には、別々のプログラム (またはライブラリ) を別々のディレクトリでコンパイルするか、プログラムやライブラリごとにテンプレートレポジトリを消去する必要があります。
定義分離テンプレート編成 (テンプレートを使用するファイルの中にテンプレートの宣言だけがあって定義はないという編成) を使用している場合には、現在のコンパイル単位にテンプレート定義が存在しないので、コンパイラが定義を検索しなければなりません。この節では、そうした検索について説明します。
定義の検索はかなり複雑で、エラーを発生しやすい傾向があります。したがって、定義検索の必要がない定義取り込み型テンプレートファイル編成を使用するようにしてください。詳細については『C++ プログラミングガイド』を参照してください。
オプションファイルで提供されるような特定の指令がない場合には、コンパイラは cfront 形式の方法でテンプレート定義ファイルを検出します。この方法では、テンプレート定義ファイルがテンプレート宣言ファイルと同じベース名を持ち、しかも現在の include パスにも存在している必要があります。たとえば、テンプレート関数 foo() が foo.h 内にある場合には、それと一致するテンプレート定義ファイルの名前を foo.cc か、または他の何らかの認識可能なソースファイル拡張子 (.C、 .c、.cc、.cpp、.cxx) にしなければなりません。テンプレート定義ファイルは、通常使用する include ディレクトリの 1 つか、またはそれと一致するヘッダーファイルと同じディレクトリの中に置かなければなりません。
-I で設定する通常の検索パスの代わりに、オプションの -ptidirectory (ディレクトリ) でテンプレート定義ファイルの検索ディレクトリを指定することができます。複数の -pti フラグは、複数の検索ディレクトリ、つまり 1 つの検索パスを定義します。 -ptidirectory を使用している場合には、コンパイラはこのパス上のテンプレート定義ファイルを探し、-I フラグを無視します。しかし、-ptidirectory フラグはソースファイルの検索規則を複雑にするので、-ptidirectory フラグより -I フラグを使用してください。
テンプレートレポジトリマネージャは、レポジトリ中のインスタンスの状態をソースファイルと確実に一致させて最新の状態にします。
たとえば、ソースファイルが -g オプション (デバッグ付き) でコンパイルされる場合には、データベースの中の必要なファイルも -g でコンパイルされます。
さらに、テンプレートレポジトリはコンパイル時の変更を追跡します。たとえば、 -DDEBUG フラグを指定して名前 DEBUG を定義すると、データベースがこれを追跡します。その次のコンパイルでこのフラグを省くと、コンパイラはこの依存性が設定されているテンプレートを再度インスタンス化します。
インスタンス化とは、C++ コンパイラがテンプレートから使用可能な関数やオブジェクトを作成するプロセスをいいます。Sun C++ 5.0 コンパイラ ではコンパイル時にインスタンス化を行います。つまり、テンプレートへの参照がコンパイルされているときに、インスタンス化が行われます。
コンパイル時のインスタンス化の長所を次に示します。
デバッグが非常に簡単である。エラーメッセージがコンテキストの中に発生するので、コンパイラが参照位置を完全に追跡することができる。
テンプレートのインスタンス化が常に最新である
リンク段階を含めて全コンパイル時間が短縮される
ソースファイルが異なるディレクトリに存在する場合、またはテンプレートシンボルを指定してライブラリを使用した場合には、テンプレートが複数回にわたってインスタンス化されることがあります。