質問
ヘッダーファイルが関数テンプレートを定義するとします。ここで、このヘッダーを #include
する2つの実装ファイルがあり、それぞれに関数テンプレートへの呼び出しがあるとします。両方の実装ファイルで、関数テンプレートは同じ型でインスタンス化されます。
// header.hh
template <typename T>
void f(const T& o)
{
// ...
}
// impl1.cc
#include "header.hh"
void fimpl1()
{
f(42);
}
// impl2.cc
#include "header.hh"
void fimpl2()
{
f(24);
}
リンカーが f()
の複数の定義について文句を言うことを期待するかもしれません。具体的には、 f()
がテンプレートにならない場合、実際にそうなります。
- リンカーが
f()
の複数の定義について文句を言わないのはなぜですか? - リンカがこの状況を適切に処理する必要があることが規格で指定されていますか?言い換えれば、上記のようなプログラムをコンパイルしてリンクすることを常に期待できますか?
- リンカが関数テンプレートのインスタンス化のセットを明確にするのに十分賢い場合、インスタンス化された関数テンプレートの場合と同じであるため、通常の関数に対して同じことができないのはなぜですか?
解決
C ++をサポートするために、リンカはそれらがすべて同じ関数であることを認識し、1つを除くすべてをスローすることを認識できるほどスマートです。
編集:説明: リンカは関数の内容を比較せず、それらが同じであると判断しません。 テンプレート化された関数はそのようにマークされ、リンカーはそれらが同じ署名を持っていることを認識します。
他のヒント
Gnu C ++コンパイラのマニュアルには、これに関する良い議論があります。 。抜粋:
C ++テンプレートは最初の言語です より多くのインテリジェンスを必要とする機能 通常よりも環境から UNIXシステムで検索します。どういうわけか コンパイラとリンカは確認する必要があります 各テンプレートインスタンスが発生すること 実行可能ファイルで一度だけ 必要であり、それ以外はまったく必要ありません。 これには2つの基本的なアプローチがあります 問題と呼ばれる ボーランドモデルとCfrontモデル。
ボーランドモデル
Borland C ++はテンプレートを解決しました 追加することによるインスタンス化の問題 共通ブロックと同等のコード それらのリンカー。コンパイラが出力します 各翻訳のテンプレートインスタンス それらを使用するユニット、およびリンカ それらを一緒に折りたたみます。利点 このモデルのリンカーは オブジェクトファイルを考慮する必要があります 自分自身;外部はありません 心配する複雑さ。この 欠点は、コンパイル時間が テンプレートコードが増加するため 繰り返しコンパイルされています。コード このモデルのために書かれた傾向があります すべてのテンプレートの定義を含める ヘッダーファイルで インスタンス化されているように見えます。
Cfrontモデル
AT&amp; T C ++トランスレーター、Cfront、 テンプレートのインスタンス化を解決しました 問題の概念を作成することにより テンプレートリポジトリ、自動的に テンプレートを維持する場所 インスタンスが保存されます。より現代的な リポジトリのバージョンは次のように機能します 次:個別のオブジェクトファイルとして 構築されると、コンパイラは テンプレート定義と で遭遇するインスタンス化 リポジトリ。リンク時に、リンク ラッパーは、 リポジトリおよび必要なコンパイル 以前はなかったインスタンス 放出された。このモデルの利点 より最適なコンパイル速度と システムリンカーを使用する機能。 ボーランドモデルを実装するには コンパイラベンダーも交換する必要があります リンカー。欠点は 大幅に複雑さが増したため、 エラーの可能性;いくつかのコードについて これは同様に透明にすることができますが、 実際には非常に難しい場合があります 複数のプログラムを1つに構築する ディレクトリと複数の1つのプログラム ディレクトリ。このために書かれたコード モデルはの定義を分離する傾向があります に非インラインメンバーテンプレート 別のファイル。 個別にコンパイル。
GNU ldバージョン2.8または 後にELFシステムで GNU / LinuxまたはSolaris 2、または Microsoft Windows、G ++は ボーランドモデル。他のシステムでは、G ++ どちらの自動モデルも実装しません。
これは、多かれ少なかれテンプレート専用の特殊なケースです。
コンパイラは、実際に使用されるテンプレートのインスタンス化のみを生成します。他のソースファイルから生成されるコードを制御できないため、メソッドがまったく生成されないように、ファイルごとにテンプレートコードを1回生成する必要があります。
これを解決するのは難しいため(標準にはテンプレート用の extern
キーワードがありますが、g ++はそれを実装しません)、リンカーは複数の定義を受け入れます。