[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.5 テンプレートはどこ

C++ のテンプレートは、普通 UNIX システムに見られるようなものよりも、 環境からより多くの情報を必要とするような言語機能の最初のものである。 コンパイラとリンカは、各テンプレートの実体は、それが必要なら 実行形式の中でただ一つしか存在すること、そしてそれ以外にはないことを なんとかして保証しなければならない。 この問題に対するアプローチには二つの基本的な方法がある。 以下では、それらを Boralnd モデルと Cfront モデルと呼ぶ。

Borland model

Borland C++ はテンプレートの実体化問題を解決するのに、 共通ブロックに等価なコードをリンカに追加した。 コンパイラは、テンプレートの実体を、それを使っているコンパイル単位毎に 出力し、リンカがそれらを一緒にまとめる。 このモデルの良いところは、リンカはオブジェクトファイルの面倒を見るだけで 良いということである。オブジェクトファイル以外には何も考えなくて良い。 このモデルの欠点は、テンプレートコードが繰り返しコンパイルされるので、 コンパイル時間が長くなることである。 このモデル向けに書いたコードはヘッダファイル中の全てのテンプレート定義を インクルードしがちである。実体化するには見えていなくてはならないからである。

Cfront model

AT&T の C++ トランスレータである Cfront はテンプレートの実体化問題を 解決するのに、テンプレートのリポジトリという概念を作り出した。 テンプレートリポジトリトは、テンプレートの実体が格納され、 自動的に維持される場所である。 より最近のバージョンのリポジトリの仕組みは以下の通りである。 個々のオブジェクトファイルが構築されると、コンパイラは見つけた テンプレートの定義と実体をどんなものでもリポジトリに置く。 リンク時に、リンクラッパがリポジトリ中のオブジェクトファイルに追加され、 まだ出力されていない実体で必要なものがコンパイルされる。 このモデルの良いところは、コンパイル速度が速いということとシステムの リンカをそのまま使えるということである。 Borland モデルを実装するには、コンパイラベンダはリンカも置き換える 必要がある。Cfront モデルの欠点は、とてつもなく複雑さが増し、 そのため問題が起きる可能性も大きくなる。コードによってはこの方法は 透過的だが、実際には一つのディレクトリで複数のプログラムを構築したり、 一つのプログラムが複数のディレクトリにわかれているプログラムを 構築するのが大変難しくなる。 このモデル向けに書いたコードはインライン展開されないメンバテンプレートを 別のファイルで分離して定義し、別ファイルを別々にコンパイルする傾向がある。

GNU ld バージョン 2.8 以降を、Linux/GNU、Solaris 2、Microsoft Windows のような ELF システムで使うときは、g++ は Borland モデルを使用する。 他のシステムでは、g++ はどちらの自動的に処理の行なわれるモデルも 実装していない。

将来の g++ は、ハイブリッドモデルをサポートする予定である。 ハイブリッドモデルでは、GCC は コンパイルに含まれるテンプレート定義 に対するどんな実体化でも生成し、テンプレート定義と実体化の コンテキスト情報をオブジェクトファイルに格納する。 リンクラッパは、必要に応じて情報を抽出し、GCC を起動して残りの 実体化を生成する。その後で、リンカが重複した実体化を組み合わせる。

現時点では、テンプレートの実体化の取扱いには以下のオプションがある。

  1. テンプレートを使用しているコードを ‘-frepo’ 付きで コンパイルする。GCC は、拡張子が ‘.rpo’ のファイルを生成する。 このファイルは、そこで実体化されうる、対応するオブジェクトファイルで 使われるテンプレートの実体化を全て列挙する。リンクのラッパである 次に、‘collect2’ が ‘.rpo’ ファイルを更新して、 GCC にそれらの実体化がどこで起きるかを知らせ、影響を受けるオブジェクト ファイルを再構築する。リンク時のオーバーヘッドは第一パスの後では 無視できる。GCC が同じファイルに実体化を置き続けるからである。

    これは、Borland モデルで書かれたアプリケーションコードには最善の 選択である。Cfront モデルで書かれたコードは、修正を行って テンプレート定義が、実体化の一つ以上の点利用可能になるようにする 必要があるだろう。普通この修正は、各テンプレートヘッダの最後に #include <tmethods.cc> を追加するだけである。

    ライブラリコードの場合、そのライブラリで必要になるテンプレートの 実体化を全て提供したいのなら、単にそのオブジェクトファイルを全て 一緒にリンクすれば良い。リンクは失敗するが、副作用として実体化が 引き起こされる。ただし、注意しなければならないのは、 複数のライブラリが同じ実体化を提供しようとすると衝突が発生する事である。 もっとうまく扱うには、次のオプションで説明される明示的な実体化を 使うこと。

  2. -fno-implicit-templates’ オプションを指定してコンパイルし、 テンプレート実体の暗黙の生成を行なわないようにし、ユーザが使用する ものを全部明示的に実体化するようにする。 この方法を取ると、他の方法を取る場合よりも、正確にどの実体が必要なのか についての知識が余計に要求される。だが、こちらの方が謎めいた点が 少ないし、ずっと扱いやすい。明示的な実体化をプログラム全体に 散らばらせることができる。それには、おそらく、実体が使われる 翻訳単位に置くか、テンプレート自体を定義している翻訳単位に置くかする。 必要な明示的な実体化を全て一つの大きなファイルに入れることができる。 あるいは、
     
    #include "Foo.h"
    #include "Foo.cc"
    
    template class Foo<int>;
    template ostream& operator <<
                    (ostream&, const Foo<int>&);
    

    上のような小さなファイルを必要な実体毎にを作ったり、これらkらテンプレート 実体化ライブラリを作ることができる。

    Cfront モデルのコードを使っている場合、メンバテンプレート定義を ‘#include’ していないファイルをコンパイルするときに ‘-fno-implicit-templates’ オプションを使うと、おそらく 回避できるだろう。

    実体化を行うのに一つの大きなファイルを使うなら、 ‘-fno-implicit-templates’ なしでコンパイルして、明示的な 実体化で必要な(しかし、他のどのファイルでも必要としない)実体を全て、 それらを指定せずに、得たいこともあるだろう。

    g++ は、Working Paper で概説されているテンプレート実体化構文を 拡張して、明示的な実体化の前方宣言と、テンプレートクラスに対し GCC がサポートするデータ(すなわち vtable)を、 そのメンバどれ一つ実体化することなしに実体化することを 可能にしている。

     
    extern template int max (int, int);
    inline template class Foo<int>;
    
  3. 何もしない。g++ が自動的な実体管理を実装しているかのように振る舞う。 Borland モデル用に書いたコードは正しく動作する。 だが、各コンパイル単位にはそれが使っているテンプレートのそれぞれの 実体を含む。大きなプログラムでは、コードの重複が 受け入れられないほどの量になるだろう。
  4. テンプレート定義を含む全てのファイルに ‘#pragma interface’ を 追加する。これらのファイルそれぞれについて、‘#include’ している 側の幾つかの ‘.C’ ファイルの先頭に、 ‘#pragma implementation "filename"’ を追加する。 次に、全てを ‘-fexternal-templates’ 付きでコンパイルする。 そうすると、テンプレートはそれを実装している(すなわち、 実装が存在するファイルに ‘#pragma implementation’ 行がある) 翻訳単位でのみ展開される。他のファイルは全て外部参照を使う。 運が良ければ、なにもかもうまく行くはずである。 未定義のシンボルがあるというエラーになったら、プログラムで使われている 各テンプレートの実体が、そのテンプレートを実装しているファイルで 使われていることを確認する必要がある。 そのファイルで特定の実体を一つも使っていないなら、単に明示的に 実体化することができる。それには、最新の C++ working paper にある、 以下のような構文を使う。
     
    template class A<int>;
    template ostream& operator << (ostream&, const A<int>&);
    

    この戦略は、どちらのモデル向けに書かれたコードでも動作する。 Cfront モデル向けに書かれたコードを使う場合は、クラステンプレートを 含むファイルとそのメンバテンプレートを含むファイルは、同じコンパイル単位で 実装されるべきである。

    ちょっとだけ違う方法として、代わりにオプション ‘-falt-external-templates’ を使う方法がある。このオプションを 指定すると、テンプレートの実体が、テンプレートが定義されているファイルを 実装する翻訳単位ではなく、最初に実体化されるヘッダを 実装している翻訳単位に生成される。このヘッダは全ての翻訳単位で 同じでなければならない。そうなってないとおそらく破綻するだろう。

    これらのプラグマについてのより詳細な議論は、 See section Declarations and Definitions in One Header


This document was generated using texi2html 1.78.