2

コンパイラは、これを行うと関数の参照が見つからないと言います。

// link.h
template <class T>
    T *Link(T *&, T *(*)())

// link.cpp
template <class T>
T c:Link(T *&ChildNodeReference, T *(*ObjectCreator)()){

}

ヘッダーのクラス内に実装すると、スムーズに進みます。

どうか、誰かが私にこれについて教えてくれるまで、私はヘッダーに取り組みます。

C ++には、奇妙なことに迷惑なものがあります。私は知っています、これなどには理由があります。それでも、コンパイラはそれについてあなたを助けることができません-_-」

4

5 に答える 5

6

テンプレートは本質的に半型安全なマクロであるため、制限があります。

通常の (非テンプレート) 関数は、オブジェクト/ライブラリ ファイルに存在するネイティブ コードにコンパイルでき、ヘッダーで利用可能なプロトタイプのみを使用して参照できます。これは、そのような関数のバージョンが 1 つしかないためです。

テンプレートを使用すると、C++ コンパイラは関数の各インスタンスを個別にコンパイルする必要があります。明らかに、関数をインスタンス化できる型のセットは事実上無制限であるため、「事前に」行うことはできません (関数を呼び出す前にコードでいつでも新しい型を定義できます)。実際、同じ関数テンプレートの 2 つのインスタンス化が完全に異なる場合があります。この極端なケースを考えてみましょう:

struct t1 {
  template <int>
  struct a {};
};

struct t2 {
  enum { a = 123 }; 
};

enum { b = 456, c = 789 };

template <class T>
void foo() {
   T::a<b>c;
}

を呼び出すとfoo<t1>()、その中のステートメントはローカル変数宣言になります。これt1::aはクラス テンプレートであるためです。

T::a<b> c;

しかし、 を呼び出すと、は整数定数foo<t2>()であるため、内部のステートメントは式になります。t2::a

(T::a < b) > c;

これは、コンパイラがテンプレートを意味のある「コンパイル」できないことを示すためのものです。実際には、ほとんどのトークンを保持する必要があります。

とはいえ、ISO C++ はテンプレートの宣言と定義を分離する機能を実際に提供しているため、.h ファイルの宣言と .cpp ファイルの定義を使用して、テンプレートを通常の関数として扱うことができます。これは、宣言と定義の両方の前にキーワードを付ける必要があるため、「テンプレートのエクスポート」と呼ばれますexport

// link.h
export template <class T>
T *Link(T *&, T *(*)());

// link.cpp
export template <class T>
T *Link(T *&ChildNodeReference, T *(*ObjectCreator)()) {
}

ただし、これは実装の負担が非常に大きいため、標準の物議を醸す機能であり、最も一般的な実装では実装を拒否しています。特に、g++、MSVC、および C++Builder はそれを実装していません。それをサポートしている私が知っている唯一のコンパイラはComeau C++です。

于 2009-10-06T23:01:23.130 に答える
5

非テンプレート コードまたは非インライン関数をヘッダーにプログラミングすることは、Bad Thing™ です。cpp ファイルを使用する理由は、特に、同じ関数コードが複数回再定義されるのを防ぐためです。

テンプレートとの違いは、コードがそのテンプレートの特殊化をインスタンス化するまで、コンパイラは実質的にそれらに触れないことです。そのため、ヘッダー内にソースを含める必要があります。

コンパイラがテンプレートの特殊化 (たとえば ) のインスタンス化を見つけるList<int>と、含まれているテンプレート コードに戻り、その特殊化でコンパイルします。これが、関数コードの再定義に関する問題がない理由です。

あなたが理解していないように見えるのは、これがテンプレート化されていないコードには当てはまらないということです。すべての非テンプレート コードは通常どおりコンパイルされるため、CPP ファイルはコードを 1 回定義してからすべてリンクするだけで済みます。

ヘッダー内で関数を定義すると、同じ関数が複数回定義されているため、リンカーはコンパイル済みの翻訳単位をリンクしません。

于 2009-10-06T22:53:26.100 に答える
4

テンプレート化された実装 (定義だけでなく) は、コンパイル時に利用可能でなければなりません。

したがって、完全なテンプレート コードは通常、ヘッダー ファイルに配置されます。

于 2009-10-06T22:30:08.440 に答える
0

テンプレート コードはヘッダーにある必要があります。すみません、完全に見落としてました!(そして、私は何年も C++ をコーディングしていると思っていました:P)

于 2009-10-06T22:30:32.863 に答える
0

戻り値の型の * を忘れました。したがって、実装は定義と一致しません。それを追加すると、動作するはずです:

T *c:Link(T *&ChildNodeReference, T *(*ObjectCreator)())
{
}

実装は、コンパイル時に使用できるようにするために、クラス定義の下のヘッダー ファイルにも含まれている必要があります。

于 2009-10-06T22:40:47.927 に答える