模板和标题问题

Jon*_*han 2 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)()){

}
Run Code Online (Sandbox Code Playgroud)

如果我在标题内的类中实现它顺利进行.

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

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

Pav*_*aev 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;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们调用foo<t1>(),它内部的语句是一个局部变量声明,因为它t1::a是一个类模板:

T::a<b> c;
Run Code Online (Sandbox Code Playgroud)

但是如果我们调用foo<t2>(),里面的语句是一个表达式,因为它t2::a是一个整数常量:

(T::a < b) > c;
Run Code Online (Sandbox Code Playgroud)

这只是为了表明编译器无法有意义地"编译"模板; 它真的必须保留令牌.

现在,所有这一切,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)()) {
}
Run Code Online (Sandbox Code Playgroud)

然而,这是标准的一个有争议的特征,因为实施的负担非常高,而且大多数流行的实现都拒绝实现它; 值得注意的是,g ++,MSVC和C++ Builder没有实现它.我所知道的唯一支持它的编译器是Comeau C++.


Nic*_*ord 5

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

与模板的不同之处在于,在您的代码实例化该模板的特化之前,编译器实际上不接触它们,这就是为什么它们需要在头部中包含它们的源.

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

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

如果在头文件中定义函数,则链接器将不会链接已编译的转换单元,因为它们已多次定义相同的函数.