写了一个模板类,类似其它类那样把定义与声明分开,结果发现只是引用.h文件根本找不到函数的定义。查看相关资料发现如下解释
- 模板是不是一个类或函数。 模板是一个“模式”,编译器用来生成的相似的类或者函数。
- 为了让编译器生成的代码,它必须同时看到模板的定义(不只是声明)和特定类型/任何用于“fill in”模板的类型。例如,如果你想使用一个foo<int>,编译器必须同时看到foo模板和你要调用具体的foo<int>。
- 编译器可能不记得另外一个.cpp文件的细节,当编译其他.cpp文件的时候。它可以 ,但大多数都没有,如果你正在阅读本FAQ,它几乎肯定不会。顺便说一句,这就是所谓的“独立编译模型”。
现在,基于这些事实,下面是一个范例,它表明为什么是这个样子。假设你有一个这样的模板Foo声明:
template<typename T> class Foo { public: Foo(); void someMethod(T x); private: T x; };
类似地,模板成员函数的定义:
template<typename T> Foo<T>::Foo() { ... } template<typename T> void Foo<T>::someMethod(T x) { ... }
现在,假设在文件Bar.cpp的一些代码要使用foo<int>:
// Bar.cpp void blah_blah_blah() { ... Foo<int> f; f.someMethod(5); ... }
显然,某人某地将不得不调用“模式”的构造函数,和someMethod()函数以及做T为int的实例化。但是,如果你把构造函数和someMethod()的定义放到文件Foo.cpp,当编译Foo.cpp时,编译器将看到模板代码;当编译Bar.cpp时,编译器将看到foo<int>。但任何时候决不会同时看到模板代码和foo<int>。因此,通过上面的2号规则,它根本不会产生foo <int>::someMethod()的代码
解决方法:
1.把声明跟定义写在一个文件
2,在使用的时候包含.h及cpp
例如,考虑foo.h头文件包含以下模板函数声明:
// File "foo.h" template<typename T> extern void foo();
现在假设文件foo.cpp实际上定义的模板函数:
// File "foo.cpp" #include <iostream> #include "foo.h" template<typename T> void foo() { std::cout << "Here I am!\n"; }
假设文件main.cpp中使用这个模板函数通过调用foo<int>():
// File "main.cpp" #include "foo.h" int main() { foo<int>(); ... }
如果你编译和(试图)链接这两个.cpp文件,大多数编译器将生成链接错误。有三种的解决方案。第一个解决方案是物理上在.h文件中定义,即使它不是一个内联函数。这种解决办法可能(或可能不会!)造成重大代码膨胀,意味着可执行文件的大小可能会显显著增加(或者,如果你的编译器足够聪明,可能不会这么做)。
另一个解决办法是保留定义在.cpp文件中,只添加行template void foo<int>()到.cpp文件:
// File "foo.cpp" #include <iostream> #include "foo.h" template<typename T> void foo() { std::cout << "Here I am!\n"; } template void foo<int>();
如果你不能修改foo.cpp,只需创建一个新的.cpp文件,例如foo-impl.cpp如下:
// File "foo-impl.cpp" #include "foo.cpp" template void foo<int>();
请注意, foo-impl.cpp文件包含.cpp文件,而不是.h文件。如果你觉着这样很乱,跳个踢踏舞,想想堪萨斯,跟着我重复,“我要这么做即使它很混乱。” 你需要信任我。如果不信任或者致使好奇,前面的FAQ给出了理由。
|