请考虑以下代码:
myclass.h:
template <class T>
struct S {
void f();
};
struct MyClass : public S<int>
{
void g();
};
myclass.cpp:
#include "myclass.h"
#include <iostream>
template <class T>
void S<T>::f()
{
std::cout << "f()\n";
/* some code here that could not be in header */
}
void MyClass::g()
{
std::cout << "g()\n";
}
main.cpp:
#include "myclass.h"
int main()
{
MyClass m;
m.g();
m.f(); // problem here
}
我有链接器错误:
未定义对`S :: f()&#39;
的引用
我可以在不将S :: f()的实现转移到头文件的情况下解决这个问题吗? 当我声明从完全专用模板基类派生的MyClass时,为什么S :: f()没有实例化?
答案 0 :(得分:1)
当我声明从完全专用模板基类派生的MyClass时,为什么S :: f()没有实例化?
因为myclass.cpp不使用该模板,并且像翻译单元中的所有未使用的模板一样,所以它们在生成的代码中没有任何用处。推导一切都很好,如果MyClass::g()
使用S<T>::f()
,你会提取代码,生活会很好。但是你没有,所以你必须以另一种方式拉它...
您应该能够通过显式实例化来完成此操作。请注意以下.cpp文件的添加内容:
#include "myclass.h"
#include <iostream>
template <class T>
void S<T>::f()
{
std::cout << "f()\n";
}
void MyClass::g()
{
std::cout << "g()\n";
}
template struct S<int>; // ADDED
然而,请注意。这仅在唯一用途为S<int>
时才有效。额外的扩展需要显式实例化的附加条目。
您也可以通过直接专业化来完成,例如将.cpp改为执行此操作:
#include "MyClass.h"
#include <iostream>
template <>
void S<int>::f()
{
std::cout << "f()\n";
}
void MyClass::g()
{
std::cout << "g()\n";
}
但这在我看来是相当有限的,因为你添加的每个附加类型都需要自己的专业化。
无论如何,祝你好运。
答案 1 :(得分:1)
添加显式实例化
template
struct S<int>;
到“myclass.cpp”使代码编译。
简而言之,S<int>
的派生只会“实例化”类定义,而不是成员。
由于f
的定义未知,因此编译器无法在此时实例化f
。
您需要为您正在使用的所有类型提供显式实例化,这在某种程度上限制了模板的实用性。
将模板定义保留在标头之外的一种流行方法是将它们放在标题末尾为#include
d的文件中。
答案 2 :(得分:0)
如果不将s :: f()函数的实现转移到头文件,则无法解决此问题。这是因为编译器在使用之前必须知道模板的实现。