编译器说我在执行此操作时找不到该函数的引用:
// link.h
template <class T>
T *Link(T *&, T *(*)())
// link.cpp
template <class T>
T c:Link(T *&ChildNodeReference, T *(*ObjectCreator)()){
}
如果我在标题内的类中实现它会很顺利。
拜托,我会在标题上工作,直到有人为此启发我。
C ++中有些东西很奇怪。我知道,这是有原因的等等。即便如此,编译器也无法帮助你解决它-_-“
答案 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)())
{
}
在类定义下的头文件中也必须实现,以便在编译时可用。