模板和标题问题

时间:2009-10-06 22:25:42

标签: c++ templates header declaration

编译器说我在执行此操作时找不到该函数的引用:

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

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

}

如果我在标题内的类中实现它会很顺利。

拜托,我会在标题上工作,直到有人为此启发我。

C ++中有些东西很奇怪。我知道,这是有原因的等等。即便如此,编译器也无法帮助你解决它-_-“

5 个答案:

答案 0 :(得分:6)

模板基本上是半类型安全的宏,因此有限制。

普通(非模板)函数可以编译为驻留对象/库文件的本机代码,然后仅使用标题中可用的原型引用,仅仅因为这样的函数只有一个版本。

使用模板,C ++编译器必须分别编译函数的每个实例。显然,它不能“提前”执行,因为您可以实例化该函数的类型集实际上是无界限的(您可以在调用函数之前始终在代码中定义新类型)。实际上,相同功能模板的两个实例可以完全不同。考虑一下这种极端情况:

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++

答案 1 :(得分:5)

在标题中编程非模板代码或非内联函数是Bad Thing™。您拥有cpp文件的原因是为了防止重复定义相同的功能代码,不止一次。

与模板的不同之处在于,在您的代码实例化该模板的特化之前,编译器实际上不会触及它们,这就是为什么他们需要将其源文件放在标题中。

当编译器找到模板特化的实例化(比如List<int>)时,它会返回到包含的模板代码并使用该特化编译它,这就是为什么你没有重新定义函数的问题代码。

您似乎不理解的是,这不适用于非模板化代码。所有非模板代码都是正常编译的,因此需要CPP文件才能定义代码,然后将它们全部链接在一起。

如果在标题内定义函数,链接器将不会链接已编译的翻译单元,因为它们已多次定义相同的函数。

答案 2 :(得分:4)

模板化实现(不仅仅是定义)必须在编译时可用。

因此,完整的模板代码通常放在头文件中。

答案 3 :(得分:0)

模板代码必须位于标题中。对不起,我完全错过了! (我以为我多年来一直在编写C ++代码:P)

答案 4 :(得分:0)

您忘记了返回类型的*。因此实现不匹配定义。添加它,它应该工作:

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

在类定义下的头文件中也必须实现,以便在编译时可用。